Index: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h =================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h (revision 284084) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h (revision 284085) @@ -1,738 +1,756 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #ifndef _DT_IMPL_H #define _DT_IMPL_H #include #include #ifndef illumos #include #include #include #include #include #endif #include #include #include #include #ifdef illumos #include #endif #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include struct dt_module; /* see below */ struct dt_pfdict; /* see */ struct dt_arg; /* see below */ struct dt_provider; /* see */ struct dt_xlator; /* see */ typedef struct dt_intrinsic { const char *din_name; /* string name of the intrinsic type */ ctf_encoding_t din_data; /* integer or floating-point CTF encoding */ uint_t din_kind; /* CTF type kind to instantiate */ } dt_intrinsic_t; typedef struct dt_typedef { const char *dty_src; /* string name of typedef source type */ const char *dty_dst; /* string name of typedef destination type */ } dt_typedef_t; typedef struct dt_intdesc { const char *did_name; /* string name of the integer type */ ctf_file_t *did_ctfp; /* CTF container for this type reference */ ctf_id_t did_type; /* CTF type reference for this type */ uintmax_t did_limit; /* maximum positive value held by type */ } dt_intdesc_t; typedef struct dt_modops { uint_t (*do_syminit)(struct dt_module *); void (*do_symsort)(struct dt_module *); GElf_Sym *(*do_symname)(struct dt_module *, const char *, GElf_Sym *, uint_t *); GElf_Sym *(*do_symaddr)(struct dt_module *, GElf_Addr, GElf_Sym *, uint_t *); } dt_modops_t; typedef struct dt_arg { int da_ndx; /* index of this argument */ int da_mapping; /* mapping of argument indices to arguments */ ctf_id_t da_type; /* type of argument */ ctf_file_t *da_ctfp; /* CTF container for type */ dt_ident_t *da_xlator; /* translator, if any */ struct dt_arg *da_next; /* next argument */ } dt_arg_t; typedef struct dt_sym { uint_t ds_symid; /* id of corresponding symbol */ uint_t ds_next; /* index of next element in hash chain */ } dt_sym_t; typedef struct dt_module { dt_list_t dm_list; /* list forward/back pointers */ char dm_name[DTRACE_MODNAMELEN]; /* string name of module */ char dm_file[MAXPATHLEN]; /* file path of module (if any) */ struct dt_module *dm_next; /* pointer to next module in hash chain */ const dt_modops_t *dm_ops; /* pointer to data model's ops vector */ Elf *dm_elf; /* libelf handle for module object */ objfs_info_t dm_info; /* object filesystem private info */ ctf_sect_t dm_symtab; /* symbol table for module */ ctf_sect_t dm_strtab; /* string table for module */ ctf_sect_t dm_ctdata; /* CTF data for module */ ctf_file_t *dm_ctfp; /* CTF container handle */ uint_t *dm_symbuckets; /* symbol table hash buckets (chain indices) */ dt_sym_t *dm_symchains; /* symbol table hash chains buffer */ void *dm_asmap; /* symbol pointers sorted by value */ uint_t dm_symfree; /* index of next free hash element */ uint_t dm_nsymbuckets; /* number of elements in bucket array */ uint_t dm_nsymelems; /* number of elements in hash table */ uint_t dm_asrsv; /* actual reserved size of dm_asmap */ uint_t dm_aslen; /* number of entries in dm_asmap */ uint_t dm_flags; /* module flags (see below) */ int dm_modid; /* modinfo(1M) module identifier */ GElf_Addr dm_text_va; /* virtual address of text section */ GElf_Xword dm_text_size; /* size in bytes of text section */ GElf_Addr dm_data_va; /* virtual address of data section */ GElf_Xword dm_data_size; /* size in bytes of data section */ GElf_Addr dm_bss_va; /* virtual address of BSS */ GElf_Xword dm_bss_size; /* size in bytes of BSS */ dt_idhash_t *dm_extern; /* external symbol definitions */ #ifndef illumos caddr_t dm_reloc_offset; /* Symbol relocation offset. */ uintptr_t *dm_sec_offsets; #endif pid_t dm_pid; /* pid for this module */ uint_t dm_nctflibs; /* number of ctf children libraries */ ctf_file_t **dm_libctfp; /* process library ctf pointers */ char **dm_libctfn; /* names of process ctf containers */ } dt_module_t; #define DT_DM_LOADED 0x1 /* module symbol and type data is loaded */ #define DT_DM_KERNEL 0x2 /* module is associated with a kernel object */ #define DT_DM_PRIMARY 0x4 /* module is a krtld primary kernel object */ +#ifdef __FreeBSD__ +/* + * A representation of a FreeBSD kernel module, used when checking module + * dependencies. This differs from dt_module_t, which refers to a KLD in the + * case of kernel probes. Since modules can be identified regardless of whether + * they've been compiled into the kernel, we use them to identify DTrace + * modules. + */ +typedef struct dt_kmodule { + struct dt_kmodule *dkm_next; /* hash table entry */ + char *dkm_name; /* string name of module */ + dt_module_t *dkm_module; /* corresponding KLD module */ +} dt_kmodule_t; +#endif + typedef struct dt_provmod { char *dp_name; /* name of provider module */ struct dt_provmod *dp_next; /* next module */ } dt_provmod_t; typedef struct dt_ahashent { struct dt_ahashent *dtahe_prev; /* prev on hash chain */ struct dt_ahashent *dtahe_next; /* next on hash chain */ struct dt_ahashent *dtahe_prevall; /* prev on list of all */ struct dt_ahashent *dtahe_nextall; /* next on list of all */ uint64_t dtahe_hashval; /* hash value */ size_t dtahe_size; /* size of data */ dtrace_aggdata_t dtahe_data; /* data */ void (*dtahe_aggregate)(int64_t *, int64_t *, size_t); /* function */ } dt_ahashent_t; typedef struct dt_ahash { dt_ahashent_t **dtah_hash; /* hash table */ dt_ahashent_t *dtah_all; /* list of all elements */ size_t dtah_size; /* size of hash table */ } dt_ahash_t; typedef struct dt_aggregate { dtrace_bufdesc_t dtat_buf; /* buf aggregation snapshot */ int dtat_flags; /* aggregate flags */ processorid_t dtat_ncpus; /* number of CPUs in aggregate */ processorid_t *dtat_cpus; /* CPUs in aggregate */ processorid_t dtat_ncpu; /* size of dtat_cpus array */ processorid_t dtat_maxcpu; /* maximum number of CPUs */ dt_ahash_t dtat_hash; /* aggregate hash table */ } dt_aggregate_t; typedef struct dt_print_aggdata { dtrace_hdl_t *dtpa_dtp; /* pointer to libdtrace handle */ dtrace_aggvarid_t dtpa_id; /* aggregation variable of interest */ FILE *dtpa_fp; /* file pointer */ int dtpa_allunprint; /* print only unprinted aggregations */ int dtpa_agghist; /* print aggregation as histogram */ int dtpa_agghisthdr; /* aggregation histogram hdr printed */ int dtpa_aggpack; /* pack quantized aggregations */ } dt_print_aggdata_t; typedef struct dt_dirpath { dt_list_t dir_list; /* linked-list forward/back pointers */ char *dir_path; /* directory pathname */ } dt_dirpath_t; typedef struct dt_lib_depend { dt_list_t dtld_deplist; /* linked-list forward/back pointers */ char *dtld_library; /* library name */ char *dtld_libpath; /* library pathname */ uint_t dtld_finish; /* completion time in tsort for lib */ uint_t dtld_start; /* starting time in tsort for lib */ uint_t dtld_loaded; /* boolean: is this library loaded */ dt_list_t dtld_dependencies; /* linked-list of lib dependencies */ dt_list_t dtld_dependents; /* linked-list of lib dependents */ } dt_lib_depend_t; typedef uint32_t dt_version_t; /* encoded version (see below) */ struct dtrace_hdl { const dtrace_vector_t *dt_vector; /* library vector, if vectored open */ void *dt_varg; /* vector argument, if vectored open */ dtrace_conf_t dt_conf; /* DTrace driver configuration profile */ char dt_errmsg[BUFSIZ]; /* buffer for formatted syntax error msgs */ const char *dt_errtag; /* tag used with last call to dt_set_errmsg() */ dt_pcb_t *dt_pcb; /* pointer to current parsing control block */ ulong_t dt_gen; /* compiler generation number */ dt_list_t dt_programs; /* linked list of dtrace_prog_t's */ dt_list_t dt_xlators; /* linked list of dt_xlator_t's */ struct dt_xlator **dt_xlatormap; /* dt_xlator_t's indexed by dx_id */ id_t dt_xlatorid; /* next dt_xlator_t id to assign */ dt_ident_t *dt_externs; /* linked list of external symbol identifiers */ dt_idhash_t *dt_macros; /* hash table of macro variable identifiers */ dt_idhash_t *dt_aggs; /* hash table of aggregation identifiers */ dt_idhash_t *dt_globals; /* hash table of global identifiers */ dt_idhash_t *dt_tls; /* hash table of thread-local identifiers */ dt_list_t dt_modlist; /* linked list of dt_module_t's */ dt_module_t **dt_mods; /* hash table of dt_module_t's */ +#ifdef __FreeBSD__ + dt_kmodule_t **dt_kmods; /* hash table of dt_kmodule_t's */ +#endif uint_t dt_modbuckets; /* number of module hash buckets */ uint_t dt_nmods; /* number of modules in hash and list */ dt_provmod_t *dt_provmod; /* linked list of provider modules */ dt_module_t *dt_exec; /* pointer to executable module */ dt_module_t *dt_rtld; /* pointer to run-time linker module */ dt_module_t *dt_cdefs; /* pointer to C dynamic type module */ dt_module_t *dt_ddefs; /* pointer to D dynamic type module */ dt_list_t dt_provlist; /* linked list of dt_provider_t's */ struct dt_provider **dt_provs; /* hash table of dt_provider_t's */ uint_t dt_provbuckets; /* number of provider hash buckets */ uint_t dt_nprovs; /* number of providers in hash and list */ dt_proc_hash_t *dt_procs; /* hash table of grabbed process handles */ char **dt_proc_env; /* additional environment variables */ dt_intdesc_t dt_ints[6]; /* cached integer type descriptions */ ctf_id_t dt_type_func; /* cached CTF identifier for function type */ ctf_id_t dt_type_fptr; /* cached CTF identifier for function pointer */ ctf_id_t dt_type_str; /* cached CTF identifier for string type */ ctf_id_t dt_type_dyn; /* cached CTF identifier for type */ ctf_id_t dt_type_stack; /* cached CTF identifier for stack type */ ctf_id_t dt_type_symaddr; /* cached CTF identifier for _symaddr type */ ctf_id_t dt_type_usymaddr; /* cached CTF ident. for _usymaddr type */ size_t dt_maxprobe; /* max enabled probe ID */ dtrace_eprobedesc_t **dt_edesc; /* enabled probe descriptions */ dtrace_probedesc_t **dt_pdesc; /* probe descriptions for enabled prbs */ size_t dt_maxagg; /* max aggregation ID */ dtrace_aggdesc_t **dt_aggdesc; /* aggregation descriptions */ int dt_maxformat; /* max format ID */ void **dt_formats; /* pointer to format array */ int dt_maxstrdata; /* max strdata ID */ char **dt_strdata; /* pointer to strdata array */ dt_aggregate_t dt_aggregate; /* aggregate */ dt_pq_t *dt_bufq; /* CPU-specific data queue */ struct dt_pfdict *dt_pfdict; /* dictionary of printf conversions */ dt_version_t dt_vmax; /* optional ceiling on program API binding */ dtrace_attribute_t dt_amin; /* optional floor on program attributes */ char *dt_cpp_path; /* pathname of cpp(1) to invoke if needed */ char **dt_cpp_argv; /* argument vector for exec'ing cpp(1) */ int dt_cpp_argc; /* count of initialized cpp(1) arguments */ int dt_cpp_args; /* size of dt_cpp_argv[] array */ char *dt_ld_path; /* pathname of ld(1) to invoke if needed */ #ifdef __FreeBSD__ char *dt_objcopy_path; /* pathname of objcopy(1) to invoke if needed */ #endif dt_list_t dt_lib_path; /* linked-list forming library search path */ uint_t dt_lazyload; /* boolean: set via -xlazyload */ uint_t dt_droptags; /* boolean: set via -xdroptags */ uint_t dt_active; /* boolean: set once tracing is active */ uint_t dt_stopped; /* boolean: set once tracing is stopped */ processorid_t dt_beganon; /* CPU that executed BEGIN probe (if any) */ processorid_t dt_endedon; /* CPU that executed END probe (if any) */ uint_t dt_oflags; /* dtrace open-time options (see dtrace.h) */ uint_t dt_cflags; /* dtrace compile-time options (see dtrace.h) */ uint_t dt_dflags; /* dtrace link-time options (see dtrace.h) */ uint_t dt_prcmode; /* dtrace process create mode (see dt_proc.h) */ uint_t dt_linkmode; /* dtrace symbol linking mode (see below) */ uint_t dt_linktype; /* dtrace link output file type (see below) */ uint_t dt_xlatemode; /* dtrace translator linking mode (see below) */ uint_t dt_stdcmode; /* dtrace stdc compatibility mode (see below) */ uint_t dt_encoding; /* dtrace output encoding (see below) */ uint_t dt_treedump; /* dtrace tree debug bitmap (see below) */ uint64_t dt_options[DTRACEOPT_MAX]; /* dtrace run-time options */ int dt_version; /* library version requested by client */ int dt_ctferr; /* error resulting from last CTF failure */ int dt_errno; /* error resulting from last failed operation */ #ifndef illumos const char *dt_errfile; int dt_errline; #endif int dt_fd; /* file descriptor for dtrace pseudo-device */ int dt_ftfd; /* file descriptor for fasttrap pseudo-device */ int dt_fterr; /* saved errno from failed open of dt_ftfd */ int dt_cdefs_fd; /* file descriptor for C CTF debugging cache */ int dt_ddefs_fd; /* file descriptor for D CTF debugging cache */ #ifdef illumos int dt_stdout_fd; /* file descriptor for saved stdout */ #else FILE *dt_freopen_fp; /* file pointer for freopened stdout */ #endif dtrace_handle_err_f *dt_errhdlr; /* error handler, if any */ void *dt_errarg; /* error handler argument */ dtrace_prog_t *dt_errprog; /* error handler program, if any */ dtrace_handle_drop_f *dt_drophdlr; /* drop handler, if any */ void *dt_droparg; /* drop handler argument */ dtrace_handle_proc_f *dt_prochdlr; /* proc handler, if any */ void *dt_procarg; /* proc handler argument */ dtrace_handle_setopt_f *dt_setopthdlr; /* setopt handler, if any */ void *dt_setoptarg; /* setopt handler argument */ dtrace_status_t dt_status[2]; /* status cache */ int dt_statusgen; /* current status generation */ hrtime_t dt_laststatus; /* last status */ hrtime_t dt_lastswitch; /* last switch of buffer data */ hrtime_t dt_lastagg; /* last snapshot of aggregation data */ char *dt_sprintf_buf; /* buffer for dtrace_sprintf() */ int dt_sprintf_buflen; /* length of dtrace_sprintf() buffer */ const char *dt_filetag; /* default filetag for dt_set_errmsg() */ char *dt_buffered_buf; /* buffer for buffered output */ size_t dt_buffered_offs; /* current offset into buffered buffer */ size_t dt_buffered_size; /* size of buffered buffer */ dtrace_handle_buffered_f *dt_bufhdlr; /* buffered handler, if any */ void *dt_bufarg; /* buffered handler argument */ dt_dof_t dt_dof; /* DOF generation buffers (see dt_dof.c) */ struct utsname dt_uts; /* uname(2) information for system */ dt_list_t dt_lib_dep; /* scratch linked-list of lib dependencies */ dt_list_t dt_lib_dep_sorted; /* dependency sorted library list */ dtrace_flowkind_t dt_flow; /* flow kind */ const char *dt_prefix; /* recommended flow prefix */ int dt_indent; /* recommended flow indent */ dtrace_epid_t dt_last_epid; /* most recently consumed EPID */ uint64_t dt_last_timestamp; /* most recently consumed timestamp */ }; /* * Values for the user arg of the ECB. */ #define DT_ECB_DEFAULT 0 #define DT_ECB_ERROR 1 /* * Values for the dt_linkmode property, which is used by the assembler when * processing external symbol references. User can set using -xlink=. */ #define DT_LINK_KERNEL 0 /* kernel syms static, user syms dynamic */ #define DT_LINK_PRIMARY 1 /* primary kernel syms static, others dynamic */ #define DT_LINK_DYNAMIC 2 /* all symbols dynamic */ #define DT_LINK_STATIC 3 /* all symbols static */ /* * Values for the dt_linktype property, which is used by dtrace_program_link() * to determine the type of output file that is desired by the client. */ #define DT_LTYP_ELF 0 /* produce ELF containing DOF */ #define DT_LTYP_DOF 1 /* produce stand-alone DOF */ /* * Values for the dt_xlatemode property, which is used to determine whether * references to dynamic translators are permitted. Set using -xlate=. */ #define DT_XL_STATIC 0 /* require xlators to be statically defined */ #define DT_XL_DYNAMIC 1 /* produce references to dynamic translators */ /* * Values for the dt_stdcmode property, which is used by the compiler when * running cpp to determine the presence and setting of the __STDC__ macro. */ #define DT_STDC_XA 0 /* ISO C + K&R C compat w/o ISO: __STDC__=0 */ #define DT_STDC_XC 1 /* Strict ISO C: __STDC__=1 */ #define DT_STDC_XS 2 /* K&R C: __STDC__ not defined */ #define DT_STDC_XT 3 /* ISO C + K&R C compat with ISO: __STDC__=0 */ /* * Values for the dt_encoding property, which is used to force a particular * character encoding (overriding default behavior and/or automatic detection). */ #define DT_ENCODING_UNSET 0 #define DT_ENCODING_ASCII 1 #define DT_ENCODING_UTF8 2 /* * Macro to test whether a given pass bit is set in the dt_treedump bit-vector. * If the bit for pass 'p' is set, the D compiler displays the parse tree for * the program by printing it to stderr at the end of compiler pass 'p'. */ #define DT_TREEDUMP_PASS(dtp, p) ((dtp)->dt_treedump & (1 << ((p) - 1))) /* * Macros for accessing the cached CTF container and type ID for the common * types "int", "string", and , which we need to use frequently in the D * compiler. The DT_INT_* macro relies upon "int" being at index 0 in the * _dtrace_ints_* tables in dt_open.c; the others are also set up there. */ #define DT_INT_CTFP(dtp) ((dtp)->dt_ints[0].did_ctfp) #define DT_INT_TYPE(dtp) ((dtp)->dt_ints[0].did_type) #define DT_FUNC_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_FUNC_TYPE(dtp) ((dtp)->dt_type_func) #define DT_FPTR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_FPTR_TYPE(dtp) ((dtp)->dt_type_fptr) #define DT_STR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_STR_TYPE(dtp) ((dtp)->dt_type_str) #define DT_DYN_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_DYN_TYPE(dtp) ((dtp)->dt_type_dyn) #define DT_STACK_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_STACK_TYPE(dtp) ((dtp)->dt_type_stack) #define DT_SYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_SYMADDR_TYPE(dtp) ((dtp)->dt_type_symaddr) #define DT_USYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_USYMADDR_TYPE(dtp) ((dtp)->dt_type_usymaddr) /* * Actions and subroutines are both DT_NODE_FUNC nodes; to avoid confusing * an action for a subroutine (or vice versa), we assure that the DT_ACT_* * constants and the DIF_SUBR_* constants occupy non-overlapping ranges by * starting the DT_ACT_* constants at DIF_SUBR_MAX + 1. */ #define DT_ACT_BASE DIF_SUBR_MAX + 1 #define DT_ACT(n) (DT_ACT_BASE + (n)) #define DT_ACT_PRINTF DT_ACT(0) /* printf() action */ #define DT_ACT_TRACE DT_ACT(1) /* trace() action */ #define DT_ACT_TRACEMEM DT_ACT(2) /* tracemem() action */ #define DT_ACT_STACK DT_ACT(3) /* stack() action */ #define DT_ACT_STOP DT_ACT(4) /* stop() action */ #define DT_ACT_BREAKPOINT DT_ACT(5) /* breakpoint() action */ #define DT_ACT_PANIC DT_ACT(6) /* panic() action */ #define DT_ACT_SPECULATE DT_ACT(7) /* speculate() action */ #define DT_ACT_COMMIT DT_ACT(8) /* commit() action */ #define DT_ACT_DISCARD DT_ACT(9) /* discard() action */ #define DT_ACT_CHILL DT_ACT(10) /* chill() action */ #define DT_ACT_EXIT DT_ACT(11) /* exit() action */ #define DT_ACT_USTACK DT_ACT(12) /* ustack() action */ #define DT_ACT_PRINTA DT_ACT(13) /* printa() action */ #define DT_ACT_RAISE DT_ACT(14) /* raise() action */ #define DT_ACT_CLEAR DT_ACT(15) /* clear() action */ #define DT_ACT_NORMALIZE DT_ACT(16) /* normalize() action */ #define DT_ACT_DENORMALIZE DT_ACT(17) /* denormalize() action */ #define DT_ACT_TRUNC DT_ACT(18) /* trunc() action */ #define DT_ACT_SYSTEM DT_ACT(19) /* system() action */ #define DT_ACT_JSTACK DT_ACT(20) /* jstack() action */ #define DT_ACT_FTRUNCATE DT_ACT(21) /* ftruncate() action */ #define DT_ACT_FREOPEN DT_ACT(22) /* freopen() action */ #define DT_ACT_SYM DT_ACT(23) /* sym()/func() actions */ #define DT_ACT_MOD DT_ACT(24) /* mod() action */ #define DT_ACT_USYM DT_ACT(25) /* usym()/ufunc() actions */ #define DT_ACT_UMOD DT_ACT(26) /* umod() action */ #define DT_ACT_UADDR DT_ACT(27) /* uaddr() action */ #define DT_ACT_SETOPT DT_ACT(28) /* setopt() action */ #define DT_ACT_PRINT DT_ACT(29) /* print() action */ #define DT_ACT_PRINTM DT_ACT(30) /* printm() action */ #define DT_ACT_PRINTT DT_ACT(31) /* printt() action */ /* * Sentinel to tell freopen() to restore the saved stdout. This must not * be ever valid for opening for write access via freopen(3C), which of * course, "." never is. */ #define DT_FREOPEN_RESTORE "." #define EDT_BASE 1000 /* base value for libdtrace errnos */ enum { EDT_VERSION = EDT_BASE, /* client is requesting unsupported version */ EDT_VERSINVAL, /* version string is invalid or overflows */ EDT_VERSUNDEF, /* requested API version is not defined */ EDT_VERSREDUCED, /* requested API version has been reduced */ EDT_CTF, /* libctf called failed (dt_ctferr has more) */ EDT_COMPILER, /* error in D program compilation */ EDT_NOTUPREG, /* tuple register allocation failure */ EDT_NOMEM, /* memory allocation failure */ EDT_INT2BIG, /* integer limit exceeded */ EDT_STR2BIG, /* string limit exceeded */ EDT_NOMOD, /* unknown module name */ EDT_NOPROV, /* unknown provider name */ EDT_NOPROBE, /* unknown probe name */ EDT_NOSYM, /* unknown symbol name */ EDT_NOSYMADDR, /* no symbol corresponds to address */ EDT_NOTYPE, /* unknown type name */ EDT_NOVAR, /* unknown variable name */ EDT_NOAGG, /* unknown aggregation name */ EDT_BADSCOPE, /* improper use of type name scoping operator */ EDT_BADSPEC, /* overspecified probe description */ EDT_BADSPCV, /* bad macro variable in probe description */ EDT_BADID, /* invalid probe identifier */ EDT_NOTLOADED, /* module is not currently loaded */ EDT_NOCTF, /* module does not contain any CTF data */ EDT_DATAMODEL, /* module and program data models don't match */ EDT_DIFVERS, /* library has newer DIF version than driver */ EDT_BADAGG, /* unrecognized aggregating action */ EDT_FIO, /* file i/o error */ EDT_DIFINVAL, /* invalid DIF program */ EDT_DIFSIZE, /* invalid DIF size */ EDT_DIFFAULT, /* failed to copyin DIF program */ EDT_BADPROBE, /* bad probe description */ EDT_BADPGLOB, /* bad probe description globbing pattern */ EDT_NOSCOPE, /* declaration scope stack underflow */ EDT_NODECL, /* declaration stack underflow */ EDT_DMISMATCH, /* record list does not match statement */ EDT_DOFFSET, /* record data offset error */ EDT_DALIGN, /* record data alignment error */ EDT_BADOPTNAME, /* invalid dtrace_setopt option name */ EDT_BADOPTVAL, /* invalid dtrace_setopt option value */ EDT_BADOPTCTX, /* invalid dtrace_setopt option context */ EDT_CPPFORK, /* failed to fork preprocessor */ EDT_CPPEXEC, /* failed to exec preprocessor */ EDT_CPPENT, /* preprocessor not found */ EDT_CPPERR, /* unknown preprocessor error */ EDT_SYMOFLOW, /* external symbol table overflow */ EDT_ACTIVE, /* operation illegal when tracing is active */ EDT_DESTRUCTIVE, /* destructive actions not allowed */ EDT_NOANON, /* no anonymous tracing state */ EDT_ISANON, /* can't claim anon state and enable probes */ EDT_ENDTOOBIG, /* END enablings exceed size of prncpl buffer */ EDT_NOCONV, /* failed to load type for printf conversion */ EDT_BADCONV, /* incomplete printf conversion */ EDT_BADERROR, /* invalid library ERROR action */ EDT_ERRABORT, /* abort due to error */ EDT_DROPABORT, /* abort due to drop */ EDT_DIRABORT, /* abort explicitly directed */ EDT_BADRVAL, /* invalid return value from callback */ EDT_BADNORMAL, /* invalid normalization */ EDT_BUFTOOSMALL, /* enabling exceeds size of buffer */ EDT_BADTRUNC, /* invalid truncation */ EDT_BUSY, /* device busy (active kernel debugger) */ EDT_ACCESS, /* insufficient privileges to use DTrace */ EDT_NOENT, /* dtrace device not available */ EDT_BRICKED, /* abort due to systemic unresponsiveness */ EDT_HARDWIRE, /* failed to load hard-wired definitions */ EDT_ELFVERSION, /* libelf is out-of-date w.r.t libdtrace */ EDT_NOBUFFERED, /* attempt to buffer output without handler */ EDT_UNSTABLE, /* description matched unstable set of probes */ EDT_BADSETOPT, /* invalid setopt library action */ EDT_BADSTACKPC, /* invalid stack program counter size */ EDT_BADAGGVAR, /* invalid aggregation variable identifier */ EDT_OVERSION, /* client is requesting deprecated version */ EDT_ENABLING_ERR, /* failed to enable probe */ EDT_NOPROBES, /* no probes sites for declared provider */ EDT_CANTLOAD /* failed to load a module */ }; /* * Interfaces for parsing and comparing DTrace attribute tuples, which describe * stability and architectural binding information. The dtrace_attribute_t * structure and associated constant definitions are found in . */ extern dtrace_attribute_t dt_attr_min(dtrace_attribute_t, dtrace_attribute_t); extern dtrace_attribute_t dt_attr_max(dtrace_attribute_t, dtrace_attribute_t); extern char *dt_attr_str(dtrace_attribute_t, char *, size_t); extern int dt_attr_cmp(dtrace_attribute_t, dtrace_attribute_t); /* * Interfaces for parsing and handling DTrace version strings. Version binding * is a feature of the D compiler that is handled completely independently of * the DTrace kernel infrastructure, so the definitions are here in libdtrace. * Version strings are compiled into an encoded uint32_t which can be compared * using C comparison operators. Version definitions are found in dt_open.c. */ #define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ #define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ #define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ #define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ #define DT_VERSION_NUMBER(M, m, u) \ ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) #define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) #define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) #define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) extern char *dt_version_num2str(dt_version_t, char *, size_t); extern int dt_version_str2num(const char *, dt_version_t *); extern int dt_version_defined(dt_version_t); /* * Miscellaneous internal libdtrace interfaces. The definitions below are for * libdtrace routines that do not yet merit their own separate header file. */ extern char *dt_cpp_add_arg(dtrace_hdl_t *, const char *); extern char *dt_cpp_pop_arg(dtrace_hdl_t *); #ifdef illumos extern int dt_set_errno(dtrace_hdl_t *, int); #else int _dt_set_errno(dtrace_hdl_t *, int, const char *, int); void dt_get_errloc(dtrace_hdl_t *, const char **, int *); #define dt_set_errno(_a,_b) _dt_set_errno(_a,_b,__FILE__,__LINE__) #endif extern void dt_set_errmsg(dtrace_hdl_t *, const char *, const char *, const char *, int, const char *, va_list); #ifdef illumos extern int dt_ioctl(dtrace_hdl_t *, int, void *); #else extern int dt_ioctl(dtrace_hdl_t *, u_long, void *); #endif extern int dt_status(dtrace_hdl_t *, processorid_t); extern long dt_sysconf(dtrace_hdl_t *, int); extern ssize_t dt_write(dtrace_hdl_t *, int, const void *, size_t); extern int dt_printf(dtrace_hdl_t *, FILE *, const char *, ...); extern void *dt_zalloc(dtrace_hdl_t *, size_t); extern void *dt_alloc(dtrace_hdl_t *, size_t); extern void dt_free(dtrace_hdl_t *, void *); extern void dt_difo_free(dtrace_hdl_t *, dtrace_difo_t *); extern int dt_gmatch(const char *, const char *); extern char *dt_basename(char *); extern ulong_t dt_popc(ulong_t); extern ulong_t dt_popcb(const ulong_t *, ulong_t); extern int dt_buffered_enable(dtrace_hdl_t *); extern int dt_buffered_flush(dtrace_hdl_t *, dtrace_probedata_t *, const dtrace_recdesc_t *, const dtrace_aggdata_t *, uint32_t flags); extern void dt_buffered_disable(dtrace_hdl_t *); extern void dt_buffered_destroy(dtrace_hdl_t *); extern uint64_t dt_stddev(uint64_t *, uint64_t); extern int dt_rw_read_held(pthread_rwlock_t *); extern int dt_rw_write_held(pthread_rwlock_t *); extern int dt_mutex_held(pthread_mutex_t *); extern int dt_options_load(dtrace_hdl_t *); #define DT_RW_READ_HELD(x) dt_rw_read_held(x) #define DT_RW_WRITE_HELD(x) dt_rw_write_held(x) #define DT_RW_LOCK_HELD(x) (DT_RW_READ_HELD(x) || DT_RW_WRITE_HELD(x)) #define DT_MUTEX_HELD(x) dt_mutex_held(x) extern void dt_dprintf(const char *, ...); extern void dt_setcontext(dtrace_hdl_t *, dtrace_probedesc_t *); extern void dt_endcontext(dtrace_hdl_t *); extern void dt_pragma(dt_node_t *); extern int dt_reduce(dtrace_hdl_t *, dt_version_t); extern void dt_cg(dt_pcb_t *, dt_node_t *); extern dtrace_difo_t *dt_as(dt_pcb_t *); extern void dt_dis(const dtrace_difo_t *, FILE *); extern int dt_aggregate_go(dtrace_hdl_t *); extern int dt_aggregate_init(dtrace_hdl_t *); extern void dt_aggregate_destroy(dtrace_hdl_t *); extern int dt_epid_lookup(dtrace_hdl_t *, dtrace_epid_t, dtrace_eprobedesc_t **, dtrace_probedesc_t **); extern void dt_epid_destroy(dtrace_hdl_t *); extern int dt_aggid_lookup(dtrace_hdl_t *, dtrace_aggid_t, dtrace_aggdesc_t **); extern void dt_aggid_destroy(dtrace_hdl_t *); extern void *dt_format_lookup(dtrace_hdl_t *, int); extern void dt_format_destroy(dtrace_hdl_t *); extern const char *dt_strdata_lookup(dtrace_hdl_t *, int); extern void dt_strdata_destroy(dtrace_hdl_t *); extern int dt_print_quantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_lquantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_llquantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_agg(const dtrace_aggdata_t *, void *); extern int dt_handle(dtrace_hdl_t *, dtrace_probedata_t *); extern int dt_handle_liberr(dtrace_hdl_t *, const dtrace_probedata_t *, const char *); extern int dt_handle_cpudrop(dtrace_hdl_t *, processorid_t, dtrace_dropkind_t, uint64_t); extern int dt_handle_status(dtrace_hdl_t *, dtrace_status_t *, dtrace_status_t *); extern int dt_handle_setopt(dtrace_hdl_t *, dtrace_setoptdata_t *); extern int dt_lib_depend_add(dtrace_hdl_t *, dt_list_t *, const char *); extern dt_lib_depend_t *dt_lib_depend_lookup(dt_list_t *, const char *); extern dt_pcb_t *yypcb; /* pointer to current parser control block */ extern char yyintprefix; /* int token prefix for macros (+/-) */ extern char yyintsuffix[4]; /* int token suffix ([uUlL]*) */ extern int yyintdecimal; /* int token is decimal (1) or octal/hex (0) */ extern char yytext[]; /* lex input buffer */ extern int yylineno; /* lex line number */ extern int yydebug; /* lex debugging */ extern dt_node_t *yypragma; /* lex token list for control lines */ extern const dtrace_attribute_t _dtrace_maxattr; /* maximum attributes */ extern const dtrace_attribute_t _dtrace_defattr; /* default attributes */ extern const dtrace_attribute_t _dtrace_symattr; /* symbol ref attributes */ extern const dtrace_attribute_t _dtrace_typattr; /* type ref attributes */ extern const dtrace_attribute_t _dtrace_prvattr; /* provider attributes */ extern const dtrace_pattr_t _dtrace_prvdesc; /* provider attribute bundle */ extern const dt_version_t _dtrace_versions[]; /* array of valid versions */ extern const char *const _dtrace_version; /* current version string */ extern int _dtrace_strbuckets; /* number of hash buckets for strings */ extern int _dtrace_intbuckets; /* number of hash buckets for ints */ extern uint_t _dtrace_stkindent; /* default indent for stack/ustack */ extern uint_t _dtrace_pidbuckets; /* number of hash buckets for pids */ extern uint_t _dtrace_pidlrulim; /* number of proc handles to cache */ extern int _dtrace_debug; /* debugging messages enabled */ extern size_t _dtrace_bufsize; /* default dt_buf_create() size */ extern int _dtrace_argmax; /* default maximum probe arguments */ extern const char *_dtrace_libdir; /* default library directory */ extern const char *_dtrace_moddir; /* default kernel module directory */ #ifdef __FreeBSD__ extern int gmatch(const char *, const char *); extern int yylex(void); #endif #ifdef __cplusplus } #endif #endif /* _DT_IMPL_H */ Index: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.c =================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.c (revision 284084) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.c (revision 284085) @@ -1,1735 +1,1785 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #include #ifdef illumos #include #include #include #include #include #include #else #include #include +#include #include #endif #include #ifdef illumos #include #endif #include #include #include #include #include #include #include #ifndef illumos #include #include #endif #include #include #include static const char *dt_module_strtab; /* active strtab for qsort callbacks */ static void dt_module_symhash_insert(dt_module_t *dmp, const char *name, uint_t id) { dt_sym_t *dsp = &dmp->dm_symchains[dmp->dm_symfree]; uint_t h; assert(dmp->dm_symfree < dmp->dm_nsymelems + 1); dsp->ds_symid = id; h = dt_strtab_hash(name, NULL) % dmp->dm_nsymbuckets; dsp->ds_next = dmp->dm_symbuckets[h]; dmp->dm_symbuckets[h] = dmp->dm_symfree++; } static uint_t dt_module_syminit32(dt_module_t *dmp) { #if STT_NUM != (STT_TLS + 1) #error "STT_NUM has grown. update dt_module_syminit32()" #endif Elf32_Sym *sym = dmp->dm_symtab.cts_data; const char *base = dmp->dm_strtab.cts_data; size_t ss_size = dmp->dm_strtab.cts_size; uint_t i, n = dmp->dm_nsymelems; uint_t asrsv = 0; #if defined(__FreeBSD__) GElf_Ehdr ehdr; int is_elf_obj; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); #endif for (i = 0; i < n; i++, sym++) { const char *name = base + sym->st_name; uchar_t type = ELF32_ST_TYPE(sym->st_info); if (type >= STT_NUM || type == STT_SECTION) continue; /* skip sections and unknown types */ if (sym->st_name == 0 || sym->st_name >= ss_size) continue; /* skip null or invalid names */ if (sym->st_value != 0 && (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) { asrsv++; /* reserve space in the address map */ #if defined(__FreeBSD__) sym->st_value += (Elf_Addr) dmp->dm_reloc_offset; if (is_elf_obj && sym->st_shndx != SHN_UNDEF && sym->st_shndx < ehdr.e_shnum) sym->st_value += dmp->dm_sec_offsets[sym->st_shndx]; #endif } dt_module_symhash_insert(dmp, name, i); } return (asrsv); } static uint_t dt_module_syminit64(dt_module_t *dmp) { #if STT_NUM != (STT_TLS + 1) #error "STT_NUM has grown. update dt_module_syminit64()" #endif Elf64_Sym *sym = dmp->dm_symtab.cts_data; const char *base = dmp->dm_strtab.cts_data; size_t ss_size = dmp->dm_strtab.cts_size; uint_t i, n = dmp->dm_nsymelems; uint_t asrsv = 0; #if defined(__FreeBSD__) GElf_Ehdr ehdr; int is_elf_obj; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); #endif for (i = 0; i < n; i++, sym++) { const char *name = base + sym->st_name; uchar_t type = ELF64_ST_TYPE(sym->st_info); if (type >= STT_NUM || type == STT_SECTION) continue; /* skip sections and unknown types */ if (sym->st_name == 0 || sym->st_name >= ss_size) continue; /* skip null or invalid names */ if (sym->st_value != 0 && (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) { asrsv++; /* reserve space in the address map */ #if defined(__FreeBSD__) sym->st_value += (Elf_Addr) dmp->dm_reloc_offset; if (is_elf_obj && sym->st_shndx != SHN_UNDEF && sym->st_shndx < ehdr.e_shnum) sym->st_value += dmp->dm_sec_offsets[sym->st_shndx]; #endif } dt_module_symhash_insert(dmp, name, i); } return (asrsv); } /* * Sort comparison function for 32-bit symbol address-to-name lookups. We sort * symbols by value. If values are equal, we prefer the symbol that is * non-zero sized, typed, not weak, or lexically first, in that order. */ static int dt_module_symcomp32(const void *lp, const void *rp) { Elf32_Sym *lhs = *((Elf32_Sym **)lp); Elf32_Sym *rhs = *((Elf32_Sym **)rp); if (lhs->st_value != rhs->st_value) return (lhs->st_value > rhs->st_value ? 1 : -1); if ((lhs->st_size == 0) != (rhs->st_size == 0)) return (lhs->st_size == 0 ? 1 : -1); if ((ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE) != (ELF32_ST_TYPE(rhs->st_info) == STT_NOTYPE)) return (ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1); if ((ELF32_ST_BIND(lhs->st_info) == STB_WEAK) != (ELF32_ST_BIND(rhs->st_info) == STB_WEAK)) return (ELF32_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1); return (strcmp(dt_module_strtab + lhs->st_name, dt_module_strtab + rhs->st_name)); } /* * Sort comparison function for 64-bit symbol address-to-name lookups. We sort * symbols by value. If values are equal, we prefer the symbol that is * non-zero sized, typed, not weak, or lexically first, in that order. */ static int dt_module_symcomp64(const void *lp, const void *rp) { Elf64_Sym *lhs = *((Elf64_Sym **)lp); Elf64_Sym *rhs = *((Elf64_Sym **)rp); if (lhs->st_value != rhs->st_value) return (lhs->st_value > rhs->st_value ? 1 : -1); if ((lhs->st_size == 0) != (rhs->st_size == 0)) return (lhs->st_size == 0 ? 1 : -1); if ((ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE) != (ELF64_ST_TYPE(rhs->st_info) == STT_NOTYPE)) return (ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1); if ((ELF64_ST_BIND(lhs->st_info) == STB_WEAK) != (ELF64_ST_BIND(rhs->st_info) == STB_WEAK)) return (ELF64_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1); return (strcmp(dt_module_strtab + lhs->st_name, dt_module_strtab + rhs->st_name)); } static void dt_module_symsort32(dt_module_t *dmp) { Elf32_Sym *symtab = (Elf32_Sym *)dmp->dm_symtab.cts_data; Elf32_Sym **sympp = (Elf32_Sym **)dmp->dm_asmap; const dt_sym_t *dsp = dmp->dm_symchains + 1; uint_t i, n = dmp->dm_symfree; for (i = 1; i < n; i++, dsp++) { Elf32_Sym *sym = symtab + dsp->ds_symid; if (sym->st_value != 0 && (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) *sympp++ = sym; } dmp->dm_aslen = (uint_t)(sympp - (Elf32_Sym **)dmp->dm_asmap); assert(dmp->dm_aslen <= dmp->dm_asrsv); dt_module_strtab = dmp->dm_strtab.cts_data; qsort(dmp->dm_asmap, dmp->dm_aslen, sizeof (Elf32_Sym *), dt_module_symcomp32); dt_module_strtab = NULL; } static void dt_module_symsort64(dt_module_t *dmp) { Elf64_Sym *symtab = (Elf64_Sym *)dmp->dm_symtab.cts_data; Elf64_Sym **sympp = (Elf64_Sym **)dmp->dm_asmap; const dt_sym_t *dsp = dmp->dm_symchains + 1; uint_t i, n = dmp->dm_symfree; for (i = 1; i < n; i++, dsp++) { Elf64_Sym *sym = symtab + dsp->ds_symid; if (sym->st_value != 0 && (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) *sympp++ = sym; } dmp->dm_aslen = (uint_t)(sympp - (Elf64_Sym **)dmp->dm_asmap); assert(dmp->dm_aslen <= dmp->dm_asrsv); dt_module_strtab = dmp->dm_strtab.cts_data; qsort(dmp->dm_asmap, dmp->dm_aslen, sizeof (Elf64_Sym *), dt_module_symcomp64); dt_module_strtab = NULL; } static GElf_Sym * dt_module_symgelf32(const Elf32_Sym *src, GElf_Sym *dst) { if (dst != NULL) { dst->st_name = src->st_name; dst->st_info = src->st_info; dst->st_other = src->st_other; dst->st_shndx = src->st_shndx; dst->st_value = src->st_value; dst->st_size = src->st_size; } return (dst); } static GElf_Sym * dt_module_symgelf64(const Elf64_Sym *src, GElf_Sym *dst) { if (dst != NULL) bcopy(src, dst, sizeof (GElf_Sym)); return (dst); } static GElf_Sym * dt_module_symname32(dt_module_t *dmp, const char *name, GElf_Sym *symp, uint_t *idp) { const Elf32_Sym *symtab = dmp->dm_symtab.cts_data; const char *strtab = dmp->dm_strtab.cts_data; const Elf32_Sym *sym; const dt_sym_t *dsp; uint_t i, h; if (dmp->dm_nsymelems == 0) return (NULL); h = dt_strtab_hash(name, NULL) % dmp->dm_nsymbuckets; for (i = dmp->dm_symbuckets[h]; i != 0; i = dsp->ds_next) { dsp = &dmp->dm_symchains[i]; sym = symtab + dsp->ds_symid; if (strcmp(name, strtab + sym->st_name) == 0) { if (idp != NULL) *idp = dsp->ds_symid; return (dt_module_symgelf32(sym, symp)); } } return (NULL); } static GElf_Sym * dt_module_symname64(dt_module_t *dmp, const char *name, GElf_Sym *symp, uint_t *idp) { const Elf64_Sym *symtab = dmp->dm_symtab.cts_data; const char *strtab = dmp->dm_strtab.cts_data; const Elf64_Sym *sym; const dt_sym_t *dsp; uint_t i, h; if (dmp->dm_nsymelems == 0) return (NULL); h = dt_strtab_hash(name, NULL) % dmp->dm_nsymbuckets; for (i = dmp->dm_symbuckets[h]; i != 0; i = dsp->ds_next) { dsp = &dmp->dm_symchains[i]; sym = symtab + dsp->ds_symid; if (strcmp(name, strtab + sym->st_name) == 0) { if (idp != NULL) *idp = dsp->ds_symid; return (dt_module_symgelf64(sym, symp)); } } return (NULL); } static GElf_Sym * dt_module_symaddr32(dt_module_t *dmp, GElf_Addr addr, GElf_Sym *symp, uint_t *idp) { const Elf32_Sym **asmap = (const Elf32_Sym **)dmp->dm_asmap; const Elf32_Sym *symtab = dmp->dm_symtab.cts_data; const Elf32_Sym *sym; uint_t i, mid, lo = 0, hi = dmp->dm_aslen - 1; Elf32_Addr v; if (dmp->dm_aslen == 0) return (NULL); while (hi - lo > 1) { mid = (lo + hi) / 2; if (addr >= asmap[mid]->st_value) lo = mid; else hi = mid; } i = addr < asmap[hi]->st_value ? lo : hi; sym = asmap[i]; v = sym->st_value; /* * If the previous entry has the same value, improve our choice. The * order of equal-valued symbols is determined by the comparison func. */ while (i-- != 0 && asmap[i]->st_value == v) sym = asmap[i]; if (addr - sym->st_value < MAX(sym->st_size, 1)) { if (idp != NULL) *idp = (uint_t)(sym - symtab); return (dt_module_symgelf32(sym, symp)); } return (NULL); } static GElf_Sym * dt_module_symaddr64(dt_module_t *dmp, GElf_Addr addr, GElf_Sym *symp, uint_t *idp) { const Elf64_Sym **asmap = (const Elf64_Sym **)dmp->dm_asmap; const Elf64_Sym *symtab = dmp->dm_symtab.cts_data; const Elf64_Sym *sym; uint_t i, mid, lo = 0, hi = dmp->dm_aslen - 1; Elf64_Addr v; if (dmp->dm_aslen == 0) return (NULL); while (hi - lo > 1) { mid = (lo + hi) / 2; if (addr >= asmap[mid]->st_value) lo = mid; else hi = mid; } i = addr < asmap[hi]->st_value ? lo : hi; sym = asmap[i]; v = sym->st_value; /* * If the previous entry has the same value, improve our choice. The * order of equal-valued symbols is determined by the comparison func. */ while (i-- != 0 && asmap[i]->st_value == v) sym = asmap[i]; if (addr - sym->st_value < MAX(sym->st_size, 1)) { if (idp != NULL) *idp = (uint_t)(sym - symtab); return (dt_module_symgelf64(sym, symp)); } return (NULL); } static const dt_modops_t dt_modops_32 = { dt_module_syminit32, dt_module_symsort32, dt_module_symname32, dt_module_symaddr32 }; static const dt_modops_t dt_modops_64 = { dt_module_syminit64, dt_module_symsort64, dt_module_symname64, dt_module_symaddr64 }; dt_module_t * dt_module_create(dtrace_hdl_t *dtp, const char *name) { long pid; char *eptr; dt_ident_t *idp; uint_t h = dt_strtab_hash(name, NULL) % dtp->dt_modbuckets; dt_module_t *dmp; for (dmp = dtp->dt_mods[h]; dmp != NULL; dmp = dmp->dm_next) { if (strcmp(dmp->dm_name, name) == 0) return (dmp); } if ((dmp = malloc(sizeof (dt_module_t))) == NULL) return (NULL); /* caller must handle allocation failure */ bzero(dmp, sizeof (dt_module_t)); (void) strlcpy(dmp->dm_name, name, sizeof (dmp->dm_name)); dt_list_append(&dtp->dt_modlist, dmp); dmp->dm_next = dtp->dt_mods[h]; dtp->dt_mods[h] = dmp; dtp->dt_nmods++; if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) dmp->dm_ops = &dt_modops_64; else dmp->dm_ops = &dt_modops_32; /* * Modules for userland processes are special. They always refer to a * specific process and have a copy of their CTF data from a specific * instant in time. Any dt_module_t that begins with 'pid' is a module * for a specific process, much like how any probe description that * begins with 'pid' is special. pid123 refers to process 123. A module * that is just 'pid' refers specifically to pid$target. This is * generally done as D does not currently allow for macros to be * evaluated when working with types. */ if (strncmp(dmp->dm_name, "pid", 3) == 0) { errno = 0; if (dmp->dm_name[3] == '\0') { idp = dt_idhash_lookup(dtp->dt_macros, "target"); if (idp != NULL && idp->di_id != 0) dmp->dm_pid = idp->di_id; } else { pid = strtol(dmp->dm_name + 3, &eptr, 10); if (errno == 0 && *eptr == '\0') dmp->dm_pid = (pid_t)pid; else dt_dprintf("encountered malformed pid " "module: %s\n", dmp->dm_name); } } return (dmp); } dt_module_t * dt_module_lookup_by_name(dtrace_hdl_t *dtp, const char *name) { uint_t h = dt_strtab_hash(name, NULL) % dtp->dt_modbuckets; dt_module_t *dmp; for (dmp = dtp->dt_mods[h]; dmp != NULL; dmp = dmp->dm_next) { if (strcmp(dmp->dm_name, name) == 0) return (dmp); } return (NULL); } /*ARGSUSED*/ dt_module_t * dt_module_lookup_by_ctf(dtrace_hdl_t *dtp, ctf_file_t *ctfp) { return (ctfp ? ctf_getspecific(ctfp) : NULL); } +#ifdef __FreeBSD__ +dt_kmodule_t * +dt_kmodule_lookup(dtrace_hdl_t *dtp, const char *name) +{ + uint_t h = dt_strtab_hash(name, NULL) % dtp->dt_modbuckets; + dt_kmodule_t *dkmp; + + for (dkmp = dtp->dt_kmods[h]; dkmp != NULL; dkmp = dkmp->dkm_next) { + if (strcmp(dkmp->dkm_name, name) == 0) + return (dkmp); + } + + return (NULL); +} +#endif + static int dt_module_load_sect(dtrace_hdl_t *dtp, dt_module_t *dmp, ctf_sect_t *ctsp) { const char *s; size_t shstrs; GElf_Shdr sh; Elf_Data *dp; Elf_Scn *sp; if (elf_getshdrstrndx(dmp->dm_elf, &shstrs) == -1) return (dt_set_errno(dtp, EDT_NOTLOADED)); for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) continue; /* skip any malformed sections */ if (sh.sh_type == ctsp->cts_type && sh.sh_entsize == ctsp->cts_entsize && strcmp(s, ctsp->cts_name) == 0) break; /* section matches specification */ } /* * If the section isn't found, return success but leave cts_data set * to NULL and cts_size set to zero for our caller. */ if (sp == NULL || (dp = elf_getdata(sp, NULL)) == NULL) return (0); #ifdef illumos ctsp->cts_data = dp->d_buf; #else if ((ctsp->cts_data = malloc(dp->d_size)) == NULL) return (0); memcpy(ctsp->cts_data, dp->d_buf, dp->d_size); #endif ctsp->cts_size = dp->d_size; dt_dprintf("loaded %s [%s] (%lu bytes)\n", dmp->dm_name, ctsp->cts_name, (ulong_t)ctsp->cts_size); return (0); } typedef struct dt_module_cb_arg { struct ps_prochandle *dpa_proc; dtrace_hdl_t *dpa_dtp; dt_module_t *dpa_dmp; uint_t dpa_count; } dt_module_cb_arg_t; /* ARGSUSED */ static int dt_module_load_proc_count(void *arg, const prmap_t *prmap, const char *obj) { ctf_file_t *fp; dt_module_cb_arg_t *dcp = arg; /* Try to grab a ctf container if it exists */ fp = Pname_to_ctf(dcp->dpa_proc, obj); if (fp != NULL) dcp->dpa_count++; return (0); } /* ARGSUSED */ static int dt_module_load_proc_build(void *arg, const prmap_t *prmap, const char *obj) { ctf_file_t *fp; char buf[MAXPATHLEN], *p; dt_module_cb_arg_t *dcp = arg; int count = dcp->dpa_count; Lmid_t lmid; fp = Pname_to_ctf(dcp->dpa_proc, obj); if (fp == NULL) return (0); fp = ctf_dup(fp); if (fp == NULL) return (0); dcp->dpa_dmp->dm_libctfp[count] = fp; /* * While it'd be nice to simply use objname here, because of our prior * actions we'll always get a resolved object name to its on disk file. * Like the pid provider, we need to tell a bit of a lie here. The type * that the user thinks of is in terms of the libraries they requested, * eg. libc.so.1, they don't care about the fact that it's * libc_hwcap.so.1. */ (void) Pobjname(dcp->dpa_proc, prmap->pr_vaddr, buf, sizeof (buf)); if ((p = strrchr(buf, '/')) == NULL) p = buf; else p++; /* * If for some reason we can't find a link map id for this module, which * would be really quite weird. We instead just say the link map id is * zero. */ if (Plmid(dcp->dpa_proc, prmap->pr_vaddr, &lmid) != 0) lmid = 0; if (lmid == 0) dcp->dpa_dmp->dm_libctfn[count] = strdup(p); else (void) asprintf(&dcp->dpa_dmp->dm_libctfn[count], "LM%x`%s", lmid, p); if (dcp->dpa_dmp->dm_libctfn[count] == NULL) return (1); ctf_setspecific(fp, dcp->dpa_dmp); dcp->dpa_count++; return (0); } /* * We've been asked to load data that belongs to another process. As such we're * going to pgrab it at this instant, load everything that we might ever care * about, and then drive on. The reason for this is that the process that we're * interested in might be changing. As long as we have grabbed it, then this * can't be a problem for us. * * For now, we're actually going to punt on most things and just try to get CTF * data, nothing else. Basically this is only useful as a source of type * information, we can't go and do the stacktrace lookups, etc. */ static int dt_module_load_proc(dtrace_hdl_t *dtp, dt_module_t *dmp) { struct ps_prochandle *p; dt_module_cb_arg_t arg; /* * Note that on success we do not release this hold. We must hold this * for our life time. */ p = dt_proc_grab(dtp, dmp->dm_pid, 0, PGRAB_RDONLY | PGRAB_FORCE); if (p == NULL) { dt_dprintf("failed to grab pid: %d\n", (int)dmp->dm_pid); return (dt_set_errno(dtp, EDT_CANTLOAD)); } dt_proc_lock(dtp, p); arg.dpa_proc = p; arg.dpa_dtp = dtp; arg.dpa_dmp = dmp; arg.dpa_count = 0; if (Pobject_iter_resolved(p, dt_module_load_proc_count, &arg) != 0) { dt_dprintf("failed to iterate objects\n"); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_CANTLOAD)); } if (arg.dpa_count == 0) { dt_dprintf("no ctf data present\n"); dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_CANTLOAD)); } dmp->dm_libctfp = malloc(sizeof (ctf_file_t *) * arg.dpa_count); if (dmp->dm_libctfp == NULL) { dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(dmp->dm_libctfp, sizeof (ctf_file_t *) * arg.dpa_count); dmp->dm_libctfn = malloc(sizeof (char *) * arg.dpa_count); if (dmp->dm_libctfn == NULL) { free(dmp->dm_libctfp); dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(dmp->dm_libctfn, sizeof (char *) * arg.dpa_count); dmp->dm_nctflibs = arg.dpa_count; arg.dpa_count = 0; if (Pobject_iter_resolved(p, dt_module_load_proc_build, &arg) != 0) { dt_proc_unlock(dtp, p); dt_module_unload(dtp, dmp); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_CANTLOAD)); } assert(arg.dpa_count == dmp->dm_nctflibs); dt_dprintf("loaded %d ctf modules for pid %d\n", arg.dpa_count, (int)dmp->dm_pid); dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); dmp->dm_flags |= DT_DM_LOADED; return (0); } int dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp) { if (dmp->dm_flags & DT_DM_LOADED) return (0); /* module is already loaded */ if (dmp->dm_pid != 0) return (dt_module_load_proc(dtp, dmp)); dmp->dm_ctdata.cts_name = ".SUNW_ctf"; dmp->dm_ctdata.cts_type = SHT_PROGBITS; dmp->dm_ctdata.cts_flags = 0; dmp->dm_ctdata.cts_data = NULL; dmp->dm_ctdata.cts_size = 0; dmp->dm_ctdata.cts_entsize = 0; dmp->dm_ctdata.cts_offset = 0; dmp->dm_symtab.cts_name = ".symtab"; dmp->dm_symtab.cts_type = SHT_SYMTAB; dmp->dm_symtab.cts_flags = 0; dmp->dm_symtab.cts_data = NULL; dmp->dm_symtab.cts_size = 0; dmp->dm_symtab.cts_entsize = dmp->dm_ops == &dt_modops_64 ? sizeof (Elf64_Sym) : sizeof (Elf32_Sym); dmp->dm_symtab.cts_offset = 0; dmp->dm_strtab.cts_name = ".strtab"; dmp->dm_strtab.cts_type = SHT_STRTAB; dmp->dm_strtab.cts_flags = 0; dmp->dm_strtab.cts_data = NULL; dmp->dm_strtab.cts_size = 0; dmp->dm_strtab.cts_entsize = 0; dmp->dm_strtab.cts_offset = 0; /* * Attempt to load the module's CTF section, symbol table section, and * string table section. Note that modules may not contain CTF data: * this will result in a successful load_sect but data of size zero. * We will then fail if dt_module_getctf() is called, as shown below. */ if (dt_module_load_sect(dtp, dmp, &dmp->dm_ctdata) == -1 || dt_module_load_sect(dtp, dmp, &dmp->dm_symtab) == -1 || dt_module_load_sect(dtp, dmp, &dmp->dm_strtab) == -1) { dt_module_unload(dtp, dmp); return (-1); /* dt_errno is set for us */ } /* * Allocate the hash chains and hash buckets for symbol name lookup. * This is relatively simple since the symbol table is of fixed size * and is known in advance. We allocate one extra element since we * use element indices instead of pointers and zero is our sentinel. */ dmp->dm_nsymelems = dmp->dm_symtab.cts_size / dmp->dm_symtab.cts_entsize; dmp->dm_nsymbuckets = _dtrace_strbuckets; dmp->dm_symfree = 1; /* first free element is index 1 */ dmp->dm_symbuckets = malloc(sizeof (uint_t) * dmp->dm_nsymbuckets); dmp->dm_symchains = malloc(sizeof (dt_sym_t) * dmp->dm_nsymelems + 1); if (dmp->dm_symbuckets == NULL || dmp->dm_symchains == NULL) { dt_module_unload(dtp, dmp); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(dmp->dm_symbuckets, sizeof (uint_t) * dmp->dm_nsymbuckets); bzero(dmp->dm_symchains, sizeof (dt_sym_t) * dmp->dm_nsymelems + 1); /* * Iterate over the symbol table data buffer and insert each symbol * name into the name hash if the name and type are valid. Then * allocate the address map, fill it in, and sort it. */ dmp->dm_asrsv = dmp->dm_ops->do_syminit(dmp); dt_dprintf("hashed %s [%s] (%u symbols)\n", dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_symfree - 1); if ((dmp->dm_asmap = malloc(sizeof (void *) * dmp->dm_asrsv)) == NULL) { dt_module_unload(dtp, dmp); return (dt_set_errno(dtp, EDT_NOMEM)); } dmp->dm_ops->do_symsort(dmp); dt_dprintf("sorted %s [%s] (%u symbols)\n", dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_aslen); dmp->dm_flags |= DT_DM_LOADED; return (0); } int dt_module_hasctf(dtrace_hdl_t *dtp, dt_module_t *dmp) { if (dmp->dm_pid != 0 && dmp->dm_nctflibs > 0) return (1); return (dt_module_getctf(dtp, dmp) != NULL); } ctf_file_t * dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp) { const char *parent; dt_module_t *pmp; ctf_file_t *pfp; int model; if (dmp->dm_ctfp != NULL || dt_module_load(dtp, dmp) != 0) return (dmp->dm_ctfp); if (dmp->dm_ops == &dt_modops_64) model = CTF_MODEL_LP64; else model = CTF_MODEL_ILP32; /* * If the data model of the module does not match our program data * model, then do not permit CTF from this module to be opened and * returned to the compiler. If we support mixed data models in the * future for combined kernel/user tracing, this can be removed. */ if (dtp->dt_conf.dtc_ctfmodel != model) { (void) dt_set_errno(dtp, EDT_DATAMODEL); return (NULL); } if (dmp->dm_ctdata.cts_size == 0) { (void) dt_set_errno(dtp, EDT_NOCTF); return (NULL); } dmp->dm_ctfp = ctf_bufopen(&dmp->dm_ctdata, &dmp->dm_symtab, &dmp->dm_strtab, &dtp->dt_ctferr); if (dmp->dm_ctfp == NULL) { (void) dt_set_errno(dtp, EDT_CTF); return (NULL); } (void) ctf_setmodel(dmp->dm_ctfp, model); ctf_setspecific(dmp->dm_ctfp, dmp); if ((parent = ctf_parent_name(dmp->dm_ctfp)) != NULL) { if ((pmp = dt_module_create(dtp, parent)) == NULL || (pfp = dt_module_getctf(dtp, pmp)) == NULL) { if (pmp == NULL) (void) dt_set_errno(dtp, EDT_NOMEM); goto err; } if (ctf_import(dmp->dm_ctfp, pfp) == CTF_ERR) { dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp); (void) dt_set_errno(dtp, EDT_CTF); goto err; } } dt_dprintf("loaded CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); return (dmp->dm_ctfp); err: ctf_close(dmp->dm_ctfp); dmp->dm_ctfp = NULL; return (NULL); } /*ARGSUSED*/ void dt_module_unload(dtrace_hdl_t *dtp, dt_module_t *dmp) { int i; ctf_close(dmp->dm_ctfp); dmp->dm_ctfp = NULL; #ifndef illumos if (dmp->dm_ctdata.cts_data != NULL) { free(dmp->dm_ctdata.cts_data); } if (dmp->dm_symtab.cts_data != NULL) { free(dmp->dm_symtab.cts_data); } if (dmp->dm_strtab.cts_data != NULL) { free(dmp->dm_strtab.cts_data); } #endif if (dmp->dm_libctfp != NULL) { for (i = 0; i < dmp->dm_nctflibs; i++) { ctf_close(dmp->dm_libctfp[i]); free(dmp->dm_libctfn[i]); } free(dmp->dm_libctfp); free(dmp->dm_libctfn); dmp->dm_libctfp = NULL; dmp->dm_nctflibs = 0; } bzero(&dmp->dm_ctdata, sizeof (ctf_sect_t)); bzero(&dmp->dm_symtab, sizeof (ctf_sect_t)); bzero(&dmp->dm_strtab, sizeof (ctf_sect_t)); if (dmp->dm_symbuckets != NULL) { free(dmp->dm_symbuckets); dmp->dm_symbuckets = NULL; } if (dmp->dm_symchains != NULL) { free(dmp->dm_symchains); dmp->dm_symchains = NULL; } if (dmp->dm_asmap != NULL) { free(dmp->dm_asmap); dmp->dm_asmap = NULL; } #if defined(__FreeBSD__) if (dmp->dm_sec_offsets != NULL) { free(dmp->dm_sec_offsets); dmp->dm_sec_offsets = NULL; } #endif dmp->dm_symfree = 0; dmp->dm_nsymbuckets = 0; dmp->dm_nsymelems = 0; dmp->dm_asrsv = 0; dmp->dm_aslen = 0; dmp->dm_text_va = 0; dmp->dm_text_size = 0; dmp->dm_data_va = 0; dmp->dm_data_size = 0; dmp->dm_bss_va = 0; dmp->dm_bss_size = 0; if (dmp->dm_extern != NULL) { dt_idhash_destroy(dmp->dm_extern); dmp->dm_extern = NULL; } (void) elf_end(dmp->dm_elf); dmp->dm_elf = NULL; dmp->dm_pid = 0; dmp->dm_flags &= ~DT_DM_LOADED; } void dt_module_destroy(dtrace_hdl_t *dtp, dt_module_t *dmp) { uint_t h = dt_strtab_hash(dmp->dm_name, NULL) % dtp->dt_modbuckets; dt_module_t **dmpp = &dtp->dt_mods[h]; dt_list_delete(&dtp->dt_modlist, dmp); assert(dtp->dt_nmods != 0); dtp->dt_nmods--; /* * Now remove this module from its hash chain. We expect to always * find the module on its hash chain, so in this loop we assert that * we don't run off the end of the list. */ while (*dmpp != dmp) { dmpp = &((*dmpp)->dm_next); assert(*dmpp != NULL); } *dmpp = dmp->dm_next; dt_module_unload(dtp, dmp); free(dmp); } /* * Insert a new external symbol reference into the specified module. The new * symbol will be marked as undefined and is assigned a symbol index beyond * any existing cached symbols from this module. We use the ident's di_data * field to store a pointer to a copy of the dtrace_syminfo_t for this symbol. */ dt_ident_t * dt_module_extern(dtrace_hdl_t *dtp, dt_module_t *dmp, const char *name, const dtrace_typeinfo_t *tip) { dtrace_syminfo_t *sip; dt_ident_t *idp; uint_t id; if (dmp->dm_extern == NULL && (dmp->dm_extern = dt_idhash_create( "extern", NULL, dmp->dm_nsymelems, UINT_MAX)) == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); return (NULL); } if (dt_idhash_nextid(dmp->dm_extern, &id) == -1) { (void) dt_set_errno(dtp, EDT_SYMOFLOW); return (NULL); } if ((sip = malloc(sizeof (dtrace_syminfo_t))) == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); return (NULL); } idp = dt_idhash_insert(dmp->dm_extern, name, DT_IDENT_SYMBOL, 0, id, _dtrace_symattr, 0, &dt_idops_thaw, NULL, dtp->dt_gen); if (idp == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); free(sip); return (NULL); } sip->dts_object = dmp->dm_name; sip->dts_name = idp->di_name; sip->dts_id = idp->di_id; idp->di_data = sip; idp->di_ctfp = tip->dtt_ctfp; idp->di_type = tip->dtt_type; return (idp); } const char * dt_module_modelname(dt_module_t *dmp) { if (dmp->dm_ops == &dt_modops_64) return ("64-bit"); else return ("32-bit"); } /* ARGSUSED */ int dt_module_getlibid(dtrace_hdl_t *dtp, dt_module_t *dmp, const ctf_file_t *fp) { int i; for (i = 0; i < dmp->dm_nctflibs; i++) { if (dmp->dm_libctfp[i] == fp) return (i); } return (-1); } /* ARGSUSED */ ctf_file_t * dt_module_getctflib(dtrace_hdl_t *dtp, dt_module_t *dmp, const char *name) { int i; for (i = 0; i < dmp->dm_nctflibs; i++) { if (strcmp(dmp->dm_libctfn[i], name) == 0) return (dmp->dm_libctfp[i]); } return (NULL); } /* * Update our module cache by adding an entry for the specified module 'name'. * We create the dt_module_t and populate it using /system/object//. * * On FreeBSD, the module name is passed as the full module file name, * including the path. */ static void #ifdef illumos dt_module_update(dtrace_hdl_t *dtp, const char *name) #else dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat) #endif { char fname[MAXPATHLEN]; struct stat64 st; int fd, err, bits; +#ifdef __FreeBSD__ + struct module_stat ms; + dt_kmodule_t *dkmp; + uint_t h; + int modid; +#endif dt_module_t *dmp; const char *s; size_t shstrs; GElf_Shdr sh; Elf_Data *dp; Elf_Scn *sp; #ifdef illumos (void) snprintf(fname, sizeof (fname), "%s/%s/object", OBJFS_ROOT, name); #else GElf_Ehdr ehdr; GElf_Phdr ph; char name[MAXPATHLEN]; uintptr_t mapbase, alignmask; int i = 0; int is_elf_obj; (void) strlcpy(name, k_stat->name, sizeof(name)); (void) strlcpy(fname, k_stat->pathname, sizeof(fname)); #endif if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 || (dmp = dt_module_create(dtp, name)) == NULL) { dt_dprintf("failed to open %s: %s\n", fname, strerror(errno)); (void) close(fd); return; } /* * Since the module can unload out from under us (and /system/object * will return ENOENT), tell libelf to cook the entire file now and * then close the underlying file descriptor immediately. If this * succeeds, we know that we can continue safely using dmp->dm_elf. */ dmp->dm_elf = elf_begin(fd, ELF_C_READ, NULL); err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD); (void) close(fd); if (dmp->dm_elf == NULL || err == -1 || elf_getshdrstrndx(dmp->dm_elf, &shstrs) == -1) { dt_dprintf("failed to load %s: %s\n", fname, elf_errmsg(elf_errno())); dt_module_destroy(dtp, dmp); return; } switch (gelf_getclass(dmp->dm_elf)) { case ELFCLASS32: dmp->dm_ops = &dt_modops_32; bits = 32; break; case ELFCLASS64: dmp->dm_ops = &dt_modops_64; bits = 64; break; default: dt_dprintf("failed to load %s: unknown ELF class\n", fname); dt_module_destroy(dtp, dmp); return; } #if defined(__FreeBSD__) mapbase = (uintptr_t)k_stat->address; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); if (is_elf_obj) { dmp->dm_sec_offsets = malloc(ehdr.e_shnum * sizeof(*dmp->dm_sec_offsets)); if (dmp->dm_sec_offsets == NULL) { dt_dprintf("failed to allocate memory\n"); dt_module_destroy(dtp, dmp); return; } } #endif /* * Iterate over the section headers locating various sections of * interest and use their attributes to flesh out the dt_module_t. */ for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) continue; /* skip any malformed sections */ #if defined(__FreeBSD__) if (sh.sh_size == 0) continue; if (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS) { alignmask = sh.sh_addralign - 1; mapbase += alignmask; mapbase &= ~alignmask; sh.sh_addr = mapbase; if (is_elf_obj) dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr; mapbase += sh.sh_size; } #endif if (strcmp(s, ".text") == 0) { dmp->dm_text_size = sh.sh_size; dmp->dm_text_va = sh.sh_addr; } else if (strcmp(s, ".data") == 0) { dmp->dm_data_size = sh.sh_size; dmp->dm_data_va = sh.sh_addr; } else if (strcmp(s, ".bss") == 0) { dmp->dm_bss_size = sh.sh_size; dmp->dm_bss_va = sh.sh_addr; } else if (strcmp(s, ".info") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { bcopy(dp->d_buf, &dmp->dm_info, MIN(sh.sh_size, sizeof (dmp->dm_info))); } else if (strcmp(s, ".filename") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { (void) strlcpy(dmp->dm_file, dp->d_buf, sizeof (dmp->dm_file)); } } dmp->dm_flags |= DT_DM_KERNEL; #ifdef illumos dmp->dm_modid = (int)OBJFS_MODID(st.st_ino); #else /* * Include .rodata and special sections into .text. * This depends on default section layout produced by GNU ld * for ELF objects and libraries: * [Text][R/O data][R/W data][Dynamic][BSS][Non loadable] */ dmp->dm_text_size = dmp->dm_data_va - dmp->dm_text_va; #if defined(__i386__) /* * Find the first load section and figure out the relocation * offset for the symbols. The kernel module will not need * relocation, but the kernel linker modules will. */ for (i = 0; gelf_getphdr(dmp->dm_elf, i, &ph) != NULL; i++) { if (ph.p_type == PT_LOAD) { dmp->dm_reloc_offset = k_stat->address - ph.p_vaddr; break; } } #endif #endif /* illumos */ if (dmp->dm_info.objfs_info_primary) dmp->dm_flags |= DT_DM_PRIMARY; + +#ifdef __FreeBSD__ + ms.version = sizeof(ms); + for (modid = kldfirstmod(k_stat->id); modid > 0; + modid = modnext(modid)) { + if (modstat(modid, &ms) != 0) { + dt_dprintf("modstat failed for id %d in %s: %s\n", + modid, k_stat->name, strerror(errno)); + continue; + } + if (dt_kmodule_lookup(dtp, ms.name) != NULL) + continue; + + dkmp = malloc(sizeof (*dkmp)); + if (dkmp == NULL) { + dt_dprintf("failed to allocate memory\n"); + dt_module_destroy(dtp, dmp); + return; + } + + h = dt_strtab_hash(ms.name, NULL) % dtp->dt_modbuckets; + dkmp->dkm_next = dtp->dt_kmods[h]; + dkmp->dkm_name = strdup(ms.name); + dkmp->dkm_module = dmp; + dtp->dt_kmods[h] = dkmp; + } +#endif dt_dprintf("opened %d-bit module %s (%s) [%d]\n", bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid); } /* * Unload all the loaded modules and then refresh the module cache with the * latest list of loaded modules and their address ranges. */ void dtrace_update(dtrace_hdl_t *dtp) { dt_module_t *dmp; DIR *dirp; #if defined(__FreeBSD__) int fileid; #endif for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) dt_module_unload(dtp, dmp); #ifdef illumos /* * Open /system/object and attempt to create a libdtrace module for * each kernel module that is loaded on the current system. */ if (!(dtp->dt_oflags & DTRACE_O_NOSYS) && (dirp = opendir(OBJFS_ROOT)) != NULL) { struct dirent *dp; while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] != '.') dt_module_update(dtp, dp->d_name); } (void) closedir(dirp); } #elif defined(__FreeBSD__) /* * Use FreeBSD's kernel loader interface to discover what kernel * modules are loaded and create a libdtrace module for each one. */ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { struct kld_file_stat k_stat; k_stat.version = sizeof(k_stat); if (kldstat(fileid, &k_stat) == 0) dt_module_update(dtp, &k_stat); } #endif /* * Look up all the macro identifiers and set di_id to the latest value. * This code collaborates with dt_lex.l on the use of di_id. We will * need to implement something fancier if we need to support non-ints. */ dt_idhash_lookup(dtp->dt_macros, "egid")->di_id = getegid(); dt_idhash_lookup(dtp->dt_macros, "euid")->di_id = geteuid(); dt_idhash_lookup(dtp->dt_macros, "gid")->di_id = getgid(); dt_idhash_lookup(dtp->dt_macros, "pid")->di_id = getpid(); dt_idhash_lookup(dtp->dt_macros, "pgid")->di_id = getpgid(0); dt_idhash_lookup(dtp->dt_macros, "ppid")->di_id = getppid(); #ifdef illumos dt_idhash_lookup(dtp->dt_macros, "projid")->di_id = getprojid(); #endif dt_idhash_lookup(dtp->dt_macros, "sid")->di_id = getsid(0); #ifdef illumos dt_idhash_lookup(dtp->dt_macros, "taskid")->di_id = gettaskid(); #endif dt_idhash_lookup(dtp->dt_macros, "uid")->di_id = getuid(); /* * Cache the pointers to the modules representing the base executable * and the run-time linker in the dtrace client handle. Note that on * x86 krtld is folded into unix, so if we don't find it, use unix * instead. */ dtp->dt_exec = dt_module_lookup_by_name(dtp, "genunix"); dtp->dt_rtld = dt_module_lookup_by_name(dtp, "krtld"); if (dtp->dt_rtld == NULL) dtp->dt_rtld = dt_module_lookup_by_name(dtp, "unix"); /* * If this is the first time we are initializing the module list, * remove the module for genunix from the module list and then move it * to the front of the module list. We do this so that type and symbol * queries encounter genunix and thereby optimize for the common case * in dtrace_lookup_by_name() and dtrace_lookup_by_type(), below. */ if (dtp->dt_exec != NULL && dtp->dt_cdefs == NULL && dtp->dt_ddefs == NULL) { dt_list_delete(&dtp->dt_modlist, dtp->dt_exec); dt_list_prepend(&dtp->dt_modlist, dtp->dt_exec); } } static dt_module_t * dt_module_from_object(dtrace_hdl_t *dtp, const char *object) { int err = EDT_NOMOD; dt_module_t *dmp; switch ((uintptr_t)object) { case (uintptr_t)DTRACE_OBJ_EXEC: dmp = dtp->dt_exec; break; case (uintptr_t)DTRACE_OBJ_RTLD: dmp = dtp->dt_rtld; break; case (uintptr_t)DTRACE_OBJ_CDEFS: dmp = dtp->dt_cdefs; break; case (uintptr_t)DTRACE_OBJ_DDEFS: dmp = dtp->dt_ddefs; break; default: dmp = dt_module_create(dtp, object); err = EDT_NOMEM; } if (dmp == NULL) (void) dt_set_errno(dtp, err); return (dmp); } /* * Exported interface to look up a symbol by name. We return the GElf_Sym and * complete symbol information for the matching symbol. */ int dtrace_lookup_by_name(dtrace_hdl_t *dtp, const char *object, const char *name, GElf_Sym *symp, dtrace_syminfo_t *sip) { dt_module_t *dmp; dt_ident_t *idp; uint_t n, id; GElf_Sym sym; uint_t mask = 0; /* mask of dt_module flags to match */ uint_t bits = 0; /* flag bits that must be present */ if (object != DTRACE_OBJ_EVERY && object != DTRACE_OBJ_KMODS && object != DTRACE_OBJ_UMODS) { if ((dmp = dt_module_from_object(dtp, object)) == NULL) return (-1); /* dt_errno is set for us */ if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ n = 1; } else { if (object == DTRACE_OBJ_KMODS) mask = bits = DT_DM_KERNEL; else if (object == DTRACE_OBJ_UMODS) mask = DT_DM_KERNEL; dmp = dt_list_next(&dtp->dt_modlist); n = dtp->dt_nmods; } if (symp == NULL) symp = &sym; for (; n > 0; n--, dmp = dt_list_next(dmp)) { if ((dmp->dm_flags & mask) != bits) continue; /* failed to match required attributes */ if (dt_module_load(dtp, dmp) == -1) continue; /* failed to load symbol table */ if (dmp->dm_ops->do_symname(dmp, name, symp, &id) != NULL) { if (sip != NULL) { sip->dts_object = dmp->dm_name; sip->dts_name = (const char *) dmp->dm_strtab.cts_data + symp->st_name; sip->dts_id = id; } return (0); } if (dmp->dm_extern != NULL && (idp = dt_idhash_lookup(dmp->dm_extern, name)) != NULL) { if (symp != &sym) { symp->st_name = (uintptr_t)idp->di_name; symp->st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); symp->st_other = 0; symp->st_shndx = SHN_UNDEF; symp->st_value = 0; symp->st_size = ctf_type_size(idp->di_ctfp, idp->di_type); } if (sip != NULL) { sip->dts_object = dmp->dm_name; sip->dts_name = idp->di_name; sip->dts_id = idp->di_id; } return (0); } } return (dt_set_errno(dtp, EDT_NOSYM)); } /* * Exported interface to look up a symbol by address. We return the GElf_Sym * and complete symbol information for the matching symbol. */ int dtrace_lookup_by_addr(dtrace_hdl_t *dtp, GElf_Addr addr, GElf_Sym *symp, dtrace_syminfo_t *sip) { dt_module_t *dmp; uint_t id; const dtrace_vector_t *v = dtp->dt_vector; if (v != NULL) return (v->dtv_lookup_by_addr(dtp->dt_varg, addr, symp, sip)); for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) { if (addr - dmp->dm_text_va < dmp->dm_text_size || addr - dmp->dm_data_va < dmp->dm_data_size || addr - dmp->dm_bss_va < dmp->dm_bss_size) break; } if (dmp == NULL) return (dt_set_errno(dtp, EDT_NOSYMADDR)); if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ if (symp != NULL) { if (dmp->dm_ops->do_symaddr(dmp, addr, symp, &id) == NULL) return (dt_set_errno(dtp, EDT_NOSYMADDR)); } if (sip != NULL) { sip->dts_object = dmp->dm_name; if (symp != NULL) { sip->dts_name = (const char *) dmp->dm_strtab.cts_data + symp->st_name; sip->dts_id = id; } else { sip->dts_name = NULL; sip->dts_id = 0; } } return (0); } int dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name, dtrace_typeinfo_t *tip) { dtrace_typeinfo_t ti; dt_module_t *dmp; int found = 0; ctf_id_t id; uint_t n, i; int justone; ctf_file_t *fp; char *buf, *p, *q; uint_t mask = 0; /* mask of dt_module flags to match */ uint_t bits = 0; /* flag bits that must be present */ if (object != DTRACE_OBJ_EVERY && object != DTRACE_OBJ_KMODS && object != DTRACE_OBJ_UMODS) { if ((dmp = dt_module_from_object(dtp, object)) == NULL) return (-1); /* dt_errno is set for us */ if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ n = 1; justone = 1; } else { if (object == DTRACE_OBJ_KMODS) mask = bits = DT_DM_KERNEL; else if (object == DTRACE_OBJ_UMODS) mask = DT_DM_KERNEL; dmp = dt_list_next(&dtp->dt_modlist); n = dtp->dt_nmods; justone = 0; } if (tip == NULL) tip = &ti; for (; n > 0; n--, dmp = dt_list_next(dmp)) { if ((dmp->dm_flags & mask) != bits) continue; /* failed to match required attributes */ /* * If we can't load the CTF container, continue on to the next * module. If our search was scoped to only one module then * return immediately leaving dt_errno unmodified. */ if (dt_module_hasctf(dtp, dmp) == 0) { if (justone) return (-1); continue; } /* * Look up the type in the module's CTF container. If our * match is a forward declaration tag, save this choice in * 'tip' and keep going in the hope that we will locate the * underlying structure definition. Otherwise just return. */ if (dmp->dm_pid == 0) { id = ctf_lookup_by_name(dmp->dm_ctfp, name); fp = dmp->dm_ctfp; } else { if ((p = strchr(name, '`')) != NULL) { buf = strdup(name); if (buf == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); p = strchr(buf, '`'); if ((q = strchr(p + 1, '`')) != NULL) p = q; *p = '\0'; fp = dt_module_getctflib(dtp, dmp, buf); if (fp == NULL || (id = ctf_lookup_by_name(fp, p + 1)) == CTF_ERR) id = CTF_ERR; free(buf); } else { for (i = 0; i < dmp->dm_nctflibs; i++) { fp = dmp->dm_libctfp[i]; id = ctf_lookup_by_name(fp, name); if (id != CTF_ERR) break; } } } if (id != CTF_ERR) { tip->dtt_object = dmp->dm_name; tip->dtt_ctfp = fp; tip->dtt_type = id; if (ctf_type_kind(fp, ctf_type_resolve(fp, id)) != CTF_K_FORWARD) return (0); found++; } } if (found == 0) return (dt_set_errno(dtp, EDT_NOTYPE)); return (0); } int dtrace_symbol_type(dtrace_hdl_t *dtp, const GElf_Sym *symp, const dtrace_syminfo_t *sip, dtrace_typeinfo_t *tip) { dt_module_t *dmp; tip->dtt_object = NULL; tip->dtt_ctfp = NULL; tip->dtt_type = CTF_ERR; tip->dtt_flags = 0; if ((dmp = dt_module_lookup_by_name(dtp, sip->dts_object)) == NULL) return (dt_set_errno(dtp, EDT_NOMOD)); if (symp->st_shndx == SHN_UNDEF && dmp->dm_extern != NULL) { dt_ident_t *idp = dt_idhash_lookup(dmp->dm_extern, sip->dts_name); if (idp == NULL) return (dt_set_errno(dtp, EDT_NOSYM)); tip->dtt_ctfp = idp->di_ctfp; tip->dtt_type = idp->di_type; } else if (GELF_ST_TYPE(symp->st_info) != STT_FUNC) { if (dt_module_getctf(dtp, dmp) == NULL) return (-1); /* errno is set for us */ tip->dtt_ctfp = dmp->dm_ctfp; tip->dtt_type = ctf_lookup_by_symbol(dmp->dm_ctfp, sip->dts_id); if (tip->dtt_type == CTF_ERR) { dtp->dt_ctferr = ctf_errno(tip->dtt_ctfp); return (dt_set_errno(dtp, EDT_CTF)); } } else { tip->dtt_ctfp = DT_FPTR_CTFP(dtp); tip->dtt_type = DT_FPTR_TYPE(dtp); } tip->dtt_object = dmp->dm_name; return (0); } static dtrace_objinfo_t * dt_module_info(const dt_module_t *dmp, dtrace_objinfo_t *dto) { dto->dto_name = dmp->dm_name; dto->dto_file = dmp->dm_file; dto->dto_id = dmp->dm_modid; dto->dto_flags = 0; if (dmp->dm_flags & DT_DM_KERNEL) dto->dto_flags |= DTRACE_OBJ_F_KERNEL; if (dmp->dm_flags & DT_DM_PRIMARY) dto->dto_flags |= DTRACE_OBJ_F_PRIMARY; dto->dto_text_va = dmp->dm_text_va; dto->dto_text_size = dmp->dm_text_size; dto->dto_data_va = dmp->dm_data_va; dto->dto_data_size = dmp->dm_data_size; dto->dto_bss_va = dmp->dm_bss_va; dto->dto_bss_size = dmp->dm_bss_size; return (dto); } int dtrace_object_iter(dtrace_hdl_t *dtp, dtrace_obj_f *func, void *data) { const dt_module_t *dmp = dt_list_next(&dtp->dt_modlist); dtrace_objinfo_t dto; int rv; for (; dmp != NULL; dmp = dt_list_next(dmp)) { if ((rv = (*func)(dtp, dt_module_info(dmp, &dto), data)) != 0) return (rv); } return (0); } int dtrace_object_info(dtrace_hdl_t *dtp, const char *object, dtrace_objinfo_t *dto) { dt_module_t *dmp; if (object == DTRACE_OBJ_EVERY || object == DTRACE_OBJ_KMODS || object == DTRACE_OBJ_UMODS || dto == NULL) return (dt_set_errno(dtp, EINVAL)); if ((dmp = dt_module_from_object(dtp, object)) == NULL) return (-1); /* dt_errno is set for us */ if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ (void) dt_module_info(dmp, dto); return (0); } Index: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.h =================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.h (revision 284084) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.h (revision 284085) @@ -1,62 +1,66 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #ifndef _DT_MODULE_H #define _DT_MODULE_H #include #ifdef __cplusplus extern "C" { #endif extern dt_module_t *dt_module_create(dtrace_hdl_t *, const char *); extern int dt_module_load(dtrace_hdl_t *, dt_module_t *); extern void dt_module_unload(dtrace_hdl_t *, dt_module_t *); extern void dt_module_destroy(dtrace_hdl_t *, dt_module_t *); extern dt_module_t *dt_module_lookup_by_name(dtrace_hdl_t *, const char *); extern dt_module_t *dt_module_lookup_by_ctf(dtrace_hdl_t *, ctf_file_t *); +#ifdef __FreeBSD__ +extern dt_kmodule_t *dt_kmodule_lookup(dtrace_hdl_t *, const char *); +#endif + extern int dt_module_hasctf(dtrace_hdl_t *, dt_module_t *); extern ctf_file_t *dt_module_getctf(dtrace_hdl_t *, dt_module_t *); extern dt_ident_t *dt_module_extern(dtrace_hdl_t *, dt_module_t *, const char *, const dtrace_typeinfo_t *); extern const char *dt_module_modelname(dt_module_t *); extern int dt_module_getlibid(dtrace_hdl_t *, dt_module_t *, const ctf_file_t *); extern ctf_file_t *dt_module_getctflib(dtrace_hdl_t *, dt_module_t *, const char *); #ifdef __cplusplus } #endif #endif /* _DT_MODULE_H */ Index: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c =================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c (revision 284084) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c (revision 284085) @@ -1,1722 +1,1742 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #ifdef illumos #include #include #else #include #include #include #endif #include #include #include #ifdef illumos #include #endif #include #include #include #include #include #include #include #define _POSIX_PTHREAD_SEMANTICS #include #undef _POSIX_PTHREAD_SEMANTICS #include #include #include #include #include #include #ifndef illumos #include #include #endif #if defined(__i386__) #include #endif /* * Stability and versioning definitions. These #defines are used in the tables * of identifiers below to fill in the attribute and version fields associated * with each identifier. The DT_ATTR_* macros are a convenience to permit more * concise declarations of common attributes such as Stable/Stable/Common. The * DT_VERS_* macros declare the encoded integer values of all versions used so * far. DT_VERS_LATEST must correspond to the latest version value among all * versions exported by the D compiler. DT_VERS_STRING must be an ASCII string * that contains DT_VERS_LATEST within it along with any suffixes (e.g. Beta). * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, * and then add the new version to the _dtrace_versions[] array declared below. * Refer to the Solaris Dynamic Tracing Guide Stability and Versioning chapters * respectively for an explanation of these DTrace features and their values. * * NOTE: Although the DTrace versioning scheme supports the labeling and * introduction of incompatible changes (e.g. dropping an interface in a * major release), the libdtrace code does not currently support this. * All versions are assumed to strictly inherit from one another. If * we ever need to provide divergent interfaces, this will need work. */ #define DT_ATTR_STABCMN { DTRACE_STABILITY_STABLE, \ DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON } #define DT_ATTR_EVOLCMN { DTRACE_STABILITY_EVOLVING, \ DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON \ } /* * The version number should be increased for every customer visible release * of DTrace. The major number should be incremented when a fundamental * change has been made that would affect all consumers, and would reflect * sweeping changes to DTrace or the D language. The minor number should be * incremented when a change is introduced that could break scripts that had * previously worked; for example, adding a new built-in variable could break * a script which was already using that identifier. The micro number should * be changed when introducing functionality changes or major bug fixes that * do not affect backward compatibility -- this is merely to make capabilities * easily determined from the version number. Minor bugs do not require any * modification to the version number. */ #define DT_VERS_1_0 DT_VERSION_NUMBER(1, 0, 0) #define DT_VERS_1_1 DT_VERSION_NUMBER(1, 1, 0) #define DT_VERS_1_2 DT_VERSION_NUMBER(1, 2, 0) #define DT_VERS_1_2_1 DT_VERSION_NUMBER(1, 2, 1) #define DT_VERS_1_2_2 DT_VERSION_NUMBER(1, 2, 2) #define DT_VERS_1_3 DT_VERSION_NUMBER(1, 3, 0) #define DT_VERS_1_4 DT_VERSION_NUMBER(1, 4, 0) #define DT_VERS_1_4_1 DT_VERSION_NUMBER(1, 4, 1) #define DT_VERS_1_5 DT_VERSION_NUMBER(1, 5, 0) #define DT_VERS_1_6 DT_VERSION_NUMBER(1, 6, 0) #define DT_VERS_1_6_1 DT_VERSION_NUMBER(1, 6, 1) #define DT_VERS_1_6_2 DT_VERSION_NUMBER(1, 6, 2) #define DT_VERS_1_6_3 DT_VERSION_NUMBER(1, 6, 3) #define DT_VERS_1_7 DT_VERSION_NUMBER(1, 7, 0) #define DT_VERS_1_7_1 DT_VERSION_NUMBER(1, 7, 1) #define DT_VERS_1_8 DT_VERSION_NUMBER(1, 8, 0) #define DT_VERS_1_8_1 DT_VERSION_NUMBER(1, 8, 1) #define DT_VERS_1_9 DT_VERSION_NUMBER(1, 9, 0) #define DT_VERS_1_9_1 DT_VERSION_NUMBER(1, 9, 1) #define DT_VERS_1_10 DT_VERSION_NUMBER(1, 10, 0) #define DT_VERS_1_11 DT_VERSION_NUMBER(1, 11, 0) #define DT_VERS_1_12 DT_VERSION_NUMBER(1, 12, 0) #define DT_VERS_1_12_1 DT_VERSION_NUMBER(1, 12, 1) #define DT_VERS_LATEST DT_VERS_1_12_1 #define DT_VERS_STRING "Sun D 1.12.1" const dt_version_t _dtrace_versions[] = { DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */ DT_VERS_1_1, /* D API 1.1.0 Solaris Express 6/05 */ DT_VERS_1_2, /* D API 1.2.0 Solaris 10 Update 1 */ DT_VERS_1_2_1, /* D API 1.2.1 Solaris Express 4/06 */ DT_VERS_1_2_2, /* D API 1.2.2 Solaris Express 6/06 */ DT_VERS_1_3, /* D API 1.3 Solaris Express 10/06 */ DT_VERS_1_4, /* D API 1.4 Solaris Express 2/07 */ DT_VERS_1_4_1, /* D API 1.4.1 Solaris Express 4/07 */ DT_VERS_1_5, /* D API 1.5 Solaris Express 7/07 */ DT_VERS_1_6, /* D API 1.6 */ DT_VERS_1_6_1, /* D API 1.6.1 */ DT_VERS_1_6_2, /* D API 1.6.2 */ DT_VERS_1_6_3, /* D API 1.6.3 */ DT_VERS_1_7, /* D API 1.7 */ DT_VERS_1_7_1, /* D API 1.7.1 */ DT_VERS_1_8, /* D API 1.8 */ DT_VERS_1_8_1, /* D API 1.8.1 */ DT_VERS_1_9, /* D API 1.9 */ DT_VERS_1_9_1, /* D API 1.9.1 */ DT_VERS_1_10, /* D API 1.10 */ DT_VERS_1_11, /* D API 1.11 */ DT_VERS_1_12, /* D API 1.12 */ DT_VERS_1_12_1, /* D API 1.12.1 */ 0 }; /* * Global variables that are formatted on FreeBSD based on the kernel file name. */ #ifndef illumos static char curthread_str[MAXPATHLEN]; static char intmtx_str[MAXPATHLEN]; static char threadmtx_str[MAXPATHLEN]; static char rwlock_str[MAXPATHLEN]; static char sxlock_str[MAXPATHLEN]; #endif /* * Table of global identifiers. This is used to populate the global identifier * hash when a new dtrace client open occurs. For more info see dt_ident.h. * The global identifiers that represent functions use the dt_idops_func ops * and specify the private data pointer as a prototype string which is parsed * when the identifier is first encountered. These prototypes look like ANSI * C function prototypes except that the special symbol "@" can be used as a * wildcard to represent a single parameter of any type (i.e. any dt_node_t). * The standard "..." notation can also be used to represent varargs. An empty * parameter list is taken to mean void (that is, no arguments are permitted). * A parameter enclosed in square brackets (e.g. "[int]") denotes an optional * argument. */ static const dt_ident_t _dtrace_globals[] = { { "alloca", DT_IDENT_FUNC, 0, DIF_SUBR_ALLOCA, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void *(size_t)" }, { "arg0", DT_IDENT_SCALAR, 0, DIF_VAR_ARG0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg1", DT_IDENT_SCALAR, 0, DIF_VAR_ARG1, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg2", DT_IDENT_SCALAR, 0, DIF_VAR_ARG2, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg3", DT_IDENT_SCALAR, 0, DIF_VAR_ARG3, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg4", DT_IDENT_SCALAR, 0, DIF_VAR_ARG4, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg5", DT_IDENT_SCALAR, 0, DIF_VAR_ARG5, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg6", DT_IDENT_SCALAR, 0, DIF_VAR_ARG6, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg7", DT_IDENT_SCALAR, 0, DIF_VAR_ARG7, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg8", DT_IDENT_SCALAR, 0, DIF_VAR_ARG8, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg9", DT_IDENT_SCALAR, 0, DIF_VAR_ARG9, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "args", DT_IDENT_ARRAY, 0, DIF_VAR_ARGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_args, NULL }, { "avg", DT_IDENT_AGGFUNC, 0, DTRACEAGG_AVG, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "basename", DT_IDENT_FUNC, 0, DIF_SUBR_BASENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "bcopy", DT_IDENT_FUNC, 0, DIF_SUBR_BCOPY, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(void *, void *, size_t)" }, { "breakpoint", DT_IDENT_ACTFUNC, 0, DT_ACT_BREAKPOINT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "caller", DT_IDENT_SCALAR, 0, DIF_VAR_CALLER, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uintptr_t" }, { "chill", DT_IDENT_ACTFUNC, 0, DT_ACT_CHILL, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "cleanpath", DT_IDENT_FUNC, 0, DIF_SUBR_CLEANPATH, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "clear", DT_IDENT_ACTFUNC, 0, DT_ACT_CLEAR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "commit", DT_IDENT_ACTFUNC, 0, DT_ACT_COMMIT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "copyin", DT_IDENT_FUNC, 0, DIF_SUBR_COPYIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void *(uintptr_t, size_t)" }, { "copyinstr", DT_IDENT_FUNC, 0, DIF_SUBR_COPYINSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(uintptr_t, [size_t])" }, { "copyinto", DT_IDENT_FUNC, 0, DIF_SUBR_COPYINTO, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(uintptr_t, size_t, void *)" }, { "copyout", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(void *, uintptr_t, size_t)" }, { "copyoutstr", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUTSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(char *, uintptr_t, size_t)" }, { "count", DT_IDENT_AGGFUNC, 0, DTRACEAGG_COUNT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "curthread", DT_IDENT_SCALAR, 0, DIF_VAR_CURTHREAD, { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_COMMON }, DT_VERS_1_0, #ifdef illumos &dt_idops_type, "genunix`kthread_t *" }, #else &dt_idops_type, curthread_str }, #endif { "ddi_pathname", DT_IDENT_FUNC, 0, DIF_SUBR_DDI_PATHNAME, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "string(void *, int64_t)" }, { "denormalize", DT_IDENT_ACTFUNC, 0, DT_ACT_DENORMALIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "dirname", DT_IDENT_FUNC, 0, DIF_SUBR_DIRNAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "discard", DT_IDENT_ACTFUNC, 0, DT_ACT_DISCARD, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "epid", DT_IDENT_SCALAR, 0, DIF_VAR_EPID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, { "errno", DT_IDENT_SCALAR, 0, DIF_VAR_ERRNO, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int" }, { "execargs", DT_IDENT_SCALAR, 0, DIF_VAR_EXECARGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "execname", DT_IDENT_SCALAR, 0, DIF_VAR_EXECNAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "exit", DT_IDENT_ACTFUNC, 0, DT_ACT_EXIT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "freopen", DT_IDENT_ACTFUNC, 0, DT_ACT_FREOPEN, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "void(@, ...)" }, { "ftruncate", DT_IDENT_ACTFUNC, 0, DT_ACT_FTRUNCATE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "func", DT_IDENT_ACTFUNC, 0, DT_ACT_SYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "getmajor", DT_IDENT_FUNC, 0, DIF_SUBR_GETMAJOR, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`major_t(genunix`dev_t)" }, { "getminor", DT_IDENT_FUNC, 0, DIF_SUBR_GETMINOR, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`minor_t(genunix`dev_t)" }, { "htonl", DT_IDENT_FUNC, 0, DIF_SUBR_HTONL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint32_t(uint32_t)" }, { "htonll", DT_IDENT_FUNC, 0, DIF_SUBR_HTONLL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint64_t(uint64_t)" }, { "htons", DT_IDENT_FUNC, 0, DIF_SUBR_HTONS, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint16_t(uint16_t)" }, { "getf", DT_IDENT_FUNC, 0, DIF_SUBR_GETF, DT_ATTR_STABCMN, DT_VERS_1_10, &dt_idops_func, "file_t *(int)" }, { "gid", DT_IDENT_SCALAR, 0, DIF_VAR_GID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "gid_t" }, { "id", DT_IDENT_SCALAR, 0, DIF_VAR_ID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, { "index", DT_IDENT_FUNC, 0, DIF_SUBR_INDEX, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "int(const char *, const char *, [int])" }, { "inet_ntoa", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA, DT_ATTR_STABCMN, #ifdef illumos DT_VERS_1_5, &dt_idops_func, "string(ipaddr_t *)" }, #else DT_VERS_1_5, &dt_idops_func, "string(in_addr_t *)" }, #endif { "inet_ntoa6", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA6, DT_ATTR_STABCMN, #ifdef illumos DT_VERS_1_5, &dt_idops_func, "string(in6_addr_t *)" }, #else DT_VERS_1_5, &dt_idops_func, "string(struct in6_addr *)" }, #endif { "inet_ntop", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOP, DT_ATTR_STABCMN, DT_VERS_1_5, &dt_idops_func, "string(int, void *)" }, { "ipl", DT_IDENT_SCALAR, 0, DIF_VAR_IPL, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, { "json", DT_IDENT_FUNC, 0, DIF_SUBR_JSON, DT_ATTR_STABCMN, DT_VERS_1_11, &dt_idops_func, "string(const char *, const char *)" }, { "jstack", DT_IDENT_ACTFUNC, 0, DT_ACT_JSTACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "lltostr", DT_IDENT_FUNC, 0, DIF_SUBR_LLTOSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(int64_t, [int])" }, { "llquantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_LLQUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_7, &dt_idops_func, "void(@, int32_t, int32_t, int32_t, int32_t, ...)" }, { "lquantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_LQUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, int32_t, int32_t, ...)" }, { "max", DT_IDENT_AGGFUNC, 0, DTRACEAGG_MAX, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "memref", DT_IDENT_FUNC, 0, DIF_SUBR_MEMREF, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "uintptr_t *(void *, size_t)" }, #ifndef illumos { "memstr", DT_IDENT_FUNC, 0, DIF_SUBR_MEMSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(void *, char, size_t)" }, #endif { "min", DT_IDENT_AGGFUNC, 0, DTRACEAGG_MIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "mod", DT_IDENT_ACTFUNC, 0, DT_ACT_MOD, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "msgdsize", DT_IDENT_FUNC, 0, DIF_SUBR_MSGDSIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "size_t(mblk_t *)" }, { "msgsize", DT_IDENT_FUNC, 0, DIF_SUBR_MSGSIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "size_t(mblk_t *)" }, #ifdef illumos { "mutex_owned", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNED, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`kmutex_t *)" }, { "mutex_owner", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`kthread_t *(genunix`kmutex_t *)" }, { "mutex_type_adaptive", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_ADAPTIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`kmutex_t *)" }, { "mutex_type_spin", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_SPIN, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`kmutex_t *)" }, #else { "mutex_owned", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNED, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, { "mutex_owner", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, threadmtx_str }, { "mutex_type_adaptive", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_ADAPTIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, { "mutex_type_spin", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_SPIN, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, #endif { "ntohl", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint32_t(uint32_t)" }, { "ntohll", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHLL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint64_t(uint64_t)" }, { "ntohs", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHS, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint16_t(uint16_t)" }, { "normalize", DT_IDENT_ACTFUNC, 0, DT_ACT_NORMALIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "panic", DT_IDENT_ACTFUNC, 0, DT_ACT_PANIC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "pid", DT_IDENT_SCALAR, 0, DIF_VAR_PID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "pid_t" }, { "ppid", DT_IDENT_SCALAR, 0, DIF_VAR_PPID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "pid_t" }, { "print", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINT, DT_ATTR_STABCMN, DT_VERS_1_9, &dt_idops_func, "void(@)" }, { "printa", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTA, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "printf", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTF, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "printm", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(size_t, uintptr_t *)" }, { "printt", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(size_t, uintptr_t *)" }, { "probefunc", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEFUNC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "probemod", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEMOD, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "probename", DT_IDENT_SCALAR, 0, DIF_VAR_PROBENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "probeprov", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEPROV, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "progenyof", DT_IDENT_FUNC, 0, DIF_SUBR_PROGENYOF, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "int(pid_t)" }, { "quantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_QUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "raise", DT_IDENT_ACTFUNC, 0, DT_ACT_RAISE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "rand", DT_IDENT_FUNC, 0, DIF_SUBR_RAND, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "int()" }, { "rindex", DT_IDENT_FUNC, 0, DIF_SUBR_RINDEX, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "int(const char *, const char *, [int])" }, #ifdef illumos { "rw_iswriter", DT_IDENT_FUNC, 0, DIF_SUBR_RW_ISWRITER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, { "rw_read_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_READ_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, { "rw_write_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_WRITE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, #else { "rw_iswriter", DT_IDENT_FUNC, 0, DIF_SUBR_RW_ISWRITER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, { "rw_read_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_READ_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, { "rw_write_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_WRITE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, #endif { "self", DT_IDENT_PTR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "void" }, { "setopt", DT_IDENT_ACTFUNC, 0, DT_ACT_SETOPT, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "void(const char *, [const char *])" }, { "speculate", DT_IDENT_ACTFUNC, 0, DT_ACT_SPECULATE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "speculation", DT_IDENT_FUNC, 0, DIF_SUBR_SPECULATION, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "int()" }, { "stack", DT_IDENT_ACTFUNC, 0, DT_ACT_STACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "stackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_STACKDEPTH, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint32_t" }, { "stddev", DT_IDENT_AGGFUNC, 0, DTRACEAGG_STDDEV, DT_ATTR_STABCMN, DT_VERS_1_6, &dt_idops_func, "void(@)" }, { "stop", DT_IDENT_ACTFUNC, 0, DT_ACT_STOP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "strchr", DT_IDENT_FUNC, 0, DIF_SUBR_STRCHR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, char)" }, { "strlen", DT_IDENT_FUNC, 0, DIF_SUBR_STRLEN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "size_t(const char *)" }, { "strjoin", DT_IDENT_FUNC, 0, DIF_SUBR_STRJOIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *, const char *)" }, { "strrchr", DT_IDENT_FUNC, 0, DIF_SUBR_STRRCHR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, char)" }, { "strstr", DT_IDENT_FUNC, 0, DIF_SUBR_STRSTR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, const char *)" }, { "strtok", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOK, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, const char *)" }, { "strtoll", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOLL, DT_ATTR_STABCMN, DT_VERS_1_11, &dt_idops_func, "int64_t(const char *, [int])" }, { "substr", DT_IDENT_FUNC, 0, DIF_SUBR_SUBSTR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, int, [int])" }, { "sum", DT_IDENT_AGGFUNC, 0, DTRACEAGG_SUM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, #ifndef illumos { "sx_isexclusive", DT_IDENT_FUNC, 0, DIF_SUBR_SX_ISEXCLUSIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, { "sx_shared_held", DT_IDENT_FUNC, 0, DIF_SUBR_SX_SHARED_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, { "sx_exclusive_held", DT_IDENT_FUNC, 0, DIF_SUBR_SX_EXCLUSIVE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, #endif { "sym", DT_IDENT_ACTFUNC, 0, DT_ACT_SYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "system", DT_IDENT_ACTFUNC, 0, DT_ACT_SYSTEM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "this", DT_IDENT_PTR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "void" }, { "tid", DT_IDENT_SCALAR, 0, DIF_VAR_TID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "id_t" }, { "timestamp", DT_IDENT_SCALAR, 0, DIF_VAR_TIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint64_t" }, { "tolower", DT_IDENT_FUNC, 0, DIF_SUBR_TOLOWER, DT_ATTR_STABCMN, DT_VERS_1_8, &dt_idops_func, "string(const char *)" }, { "toupper", DT_IDENT_FUNC, 0, DIF_SUBR_TOUPPER, DT_ATTR_STABCMN, DT_VERS_1_8, &dt_idops_func, "string(const char *)" }, { "trace", DT_IDENT_ACTFUNC, 0, DT_ACT_TRACE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "tracemem", DT_IDENT_ACTFUNC, 0, DT_ACT_TRACEMEM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, size_t, ...)" }, { "trunc", DT_IDENT_ACTFUNC, 0, DT_ACT_TRUNC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "typeref", DT_IDENT_FUNC, 0, DIF_SUBR_TYPEREF, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "uintptr_t *(void *, size_t, string, size_t)" }, { "uaddr", DT_IDENT_ACTFUNC, 0, DT_ACT_UADDR, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, { "ucaller", DT_IDENT_SCALAR, 0, DIF_VAR_UCALLER, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_type, "uint64_t" }, { "ufunc", DT_IDENT_ACTFUNC, 0, DT_ACT_USYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, { "uid", DT_IDENT_SCALAR, 0, DIF_VAR_UID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uid_t" }, { "umod", DT_IDENT_ACTFUNC, 0, DT_ACT_UMOD, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, { "uregs", DT_IDENT_ARRAY, 0, DIF_VAR_UREGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_regs, NULL }, { "ustack", DT_IDENT_ACTFUNC, 0, DT_ACT_USTACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "ustackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_USTACKDEPTH, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_type, "uint32_t" }, { "usym", DT_IDENT_ACTFUNC, 0, DT_ACT_USYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, { "vtimestamp", DT_IDENT_SCALAR, 0, DIF_VAR_VTIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint64_t" }, { "walltimestamp", DT_IDENT_SCALAR, 0, DIF_VAR_WALLTIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, #ifdef illumos { "zonename", DT_IDENT_SCALAR, 0, DIF_VAR_ZONENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, #endif #ifndef illumos { "cpu", DT_IDENT_SCALAR, 0, DIF_VAR_CPU, DT_ATTR_STABCMN, DT_VERS_1_6_3, &dt_idops_type, "int" }, #endif { NULL, 0, 0, 0, { 0, 0, 0 }, 0, NULL, NULL } }; /* * Tables of ILP32 intrinsic integer and floating-point type templates to use * to populate the dynamic "C" CTF type container. */ static const dt_intrinsic_t _dtrace_intrinsics_32[] = { { "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER }, { "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "unsigned", { 0, 0, 32 }, CTF_K_INTEGER }, { "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER }, { "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER }, { "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER }, { "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT }, { "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT }, { "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT }, { "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT }, { "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT }, { "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT }, { "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT }, { "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT }, { "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT }, { NULL, { 0, 0, 0 }, 0 } }; /* * Tables of LP64 intrinsic integer and floating-point type templates to use * to populate the dynamic "C" CTF type container. */ static const dt_intrinsic_t _dtrace_intrinsics_64[] = { { "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER }, { "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "unsigned", { 0, 0, 32 }, CTF_K_INTEGER }, { "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER }, { "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long", { 0, 0, 64 }, CTF_K_INTEGER }, { "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER }, { "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER }, { "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT }, { "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT }, { "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT }, { "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT }, { "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT }, { "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT }, { "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT }, { "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT }, { "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT }, { NULL, { 0, 0, 0 }, 0 } }; /* * Tables of ILP32 typedefs to use to populate the dynamic "D" CTF container. * These aliases ensure that D definitions can use typical names. */ static const dt_typedef_t _dtrace_typedefs_32[] = { { "char", "int8_t" }, { "short", "int16_t" }, { "int", "int32_t" }, { "long long", "int64_t" }, { "int", "intptr_t" }, { "int", "ssize_t" }, { "unsigned char", "uint8_t" }, { "unsigned short", "uint16_t" }, { "unsigned", "uint32_t" }, { "unsigned long long", "uint64_t" }, { "unsigned char", "uchar_t" }, { "unsigned short", "ushort_t" }, { "unsigned", "uint_t" }, { "unsigned long", "ulong_t" }, { "unsigned long long", "u_longlong_t" }, { "int", "ptrdiff_t" }, { "unsigned", "uintptr_t" }, { "unsigned", "size_t" }, { "long", "id_t" }, { "long", "pid_t" }, { NULL, NULL } }; /* * Tables of LP64 typedefs to use to populate the dynamic "D" CTF container. * These aliases ensure that D definitions can use typical names. */ static const dt_typedef_t _dtrace_typedefs_64[] = { { "char", "int8_t" }, { "short", "int16_t" }, { "int", "int32_t" }, { "long", "int64_t" }, { "long", "intptr_t" }, { "long", "ssize_t" }, { "unsigned char", "uint8_t" }, { "unsigned short", "uint16_t" }, { "unsigned", "uint32_t" }, { "unsigned long", "uint64_t" }, { "unsigned char", "uchar_t" }, { "unsigned short", "ushort_t" }, { "unsigned", "uint_t" }, { "unsigned long", "ulong_t" }, { "unsigned long long", "u_longlong_t" }, { "long", "ptrdiff_t" }, { "unsigned long", "uintptr_t" }, { "unsigned long", "size_t" }, { "int", "id_t" }, { "int", "pid_t" }, { NULL, NULL } }; /* * Tables of ILP32 integer type templates used to populate the dtp->dt_ints[] * cache when a new dtrace client open occurs. Values are set by dtrace_open(). */ static const dt_intdesc_t _dtrace_ints_32[] = { { "int", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned int", NULL, CTF_ERR, 0xffffffffULL }, { "long", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned long", NULL, CTF_ERR, 0xffffffffULL }, { "long long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long long", NULL, CTF_ERR, 0xffffffffffffffffULL } }; /* * Tables of LP64 integer type templates used to populate the dtp->dt_ints[] * cache when a new dtrace client open occurs. Values are set by dtrace_open(). */ static const dt_intdesc_t _dtrace_ints_64[] = { { "int", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned int", NULL, CTF_ERR, 0xffffffffULL }, { "long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long", NULL, CTF_ERR, 0xffffffffffffffffULL }, { "long long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long long", NULL, CTF_ERR, 0xffffffffffffffffULL } }; /* * Table of macro variable templates used to populate the macro identifier hash * when a new dtrace client open occurs. Values are set by dtrace_update(). */ static const dt_ident_t _dtrace_macros[] = { { "egid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "euid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "gid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "pid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "pgid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "ppid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "projid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "sid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "taskid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "target", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "uid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { NULL, 0, 0, 0, { 0, 0, 0 }, 0 } }; /* * Hard-wired definition string to be compiled and cached every time a new * DTrace library handle is initialized. This string should only be used to * contain definitions that should be present regardless of DTRACE_O_NOLIBS. */ static const char _dtrace_hardwire[] = "\ inline long NULL = 0; \n\ #pragma D binding \"1.0\" NULL\n\ "; /* * Default DTrace configuration to use when opening libdtrace DTRACE_O_NODEV. * If DTRACE_O_NODEV is not set, we load the configuration from the kernel. * The use of CTF_MODEL_NATIVE is more subtle than it might appear: we are * relying on the fact that when running dtrace(1M), isaexec will invoke the * binary with the same bitness as the kernel, which is what we want by default * when generating our DIF. The user can override the choice using oflags. */ static const dtrace_conf_t _dtrace_conf = { DIF_VERSION, /* dtc_difversion */ DIF_DIR_NREGS, /* dtc_difintregs */ DIF_DTR_NREGS, /* dtc_diftupregs */ CTF_MODEL_NATIVE /* dtc_ctfmodel */ }; const dtrace_attribute_t _dtrace_maxattr = { DTRACE_STABILITY_MAX, DTRACE_STABILITY_MAX, DTRACE_CLASS_MAX }; const dtrace_attribute_t _dtrace_defattr = { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }; const dtrace_attribute_t _dtrace_symattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_attribute_t _dtrace_typattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_attribute_t _dtrace_prvattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_pattr_t _dtrace_prvdesc = { { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, }; #ifdef illumos const char *_dtrace_defcpp = "/usr/ccs/lib/cpp"; /* default cpp(1) to invoke */ const char *_dtrace_defld = "/usr/ccs/bin/ld"; /* default ld(1) to invoke */ #else const char *_dtrace_defcpp = "cpp"; /* default cpp(1) to invoke */ const char *_dtrace_defld = "ld"; /* default ld(1) to invoke */ const char *_dtrace_defobjcopy = "objcopy"; /* default objcopy(1) to invoke */ #endif const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */ #ifdef illumos const char *_dtrace_provdir = "/dev/dtrace/provider"; /* provider directory */ #else const char *_dtrace_libdir32 = "/usr/lib32/dtrace"; const char *_dtrace_provdir = "/dev/dtrace"; /* provider directory */ #endif int _dtrace_strbuckets = 211; /* default number of hash buckets (prime) */ int _dtrace_intbuckets = 256; /* default number of integer buckets (Pof2) */ uint_t _dtrace_strsize = 256; /* default size of string intrinsic type */ uint_t _dtrace_stkindent = 14; /* default whitespace indent for stack/ustack */ uint_t _dtrace_pidbuckets = 64; /* default number of pid hash buckets */ uint_t _dtrace_pidlrulim = 8; /* default number of pid handles to cache */ size_t _dtrace_bufsize = 512; /* default dt_buf_create() size */ int _dtrace_argmax = 32; /* default maximum number of probe arguments */ int _dtrace_debug = 0; /* debug messages enabled (off) */ const char *const _dtrace_version = DT_VERS_STRING; /* API version string */ int _dtrace_rdvers = RD_VERSION; /* rtld_db feature version */ typedef struct dt_fdlist { int *df_fds; /* array of provider driver file descriptors */ uint_t df_ents; /* number of valid elements in df_fds[] */ uint_t df_size; /* size of df_fds[] */ } dt_fdlist_t; #ifdef illumos #pragma init(_dtrace_init) #else void _dtrace_init(void) __attribute__ ((constructor)); #endif void _dtrace_init(void) { _dtrace_debug = getenv("DTRACE_DEBUG") != NULL; for (; _dtrace_rdvers > 0; _dtrace_rdvers--) { if (rd_init(_dtrace_rdvers) == RD_OK) break; } #if defined(__i386__) /* make long doubles 64 bits -sson */ (void) fpsetprec(FP_PE); #endif } static dtrace_hdl_t * set_open_errno(dtrace_hdl_t *dtp, int *errp, int err) { if (dtp != NULL) dtrace_close(dtp); if (errp != NULL) *errp = err; return (NULL); } static void dt_provmod_open(dt_provmod_t **provmod, dt_fdlist_t *dfp) { dt_provmod_t *prov; char path[PATH_MAX]; int fd; #ifdef illumos struct dirent *dp, *ep; DIR *dirp; if ((dirp = opendir(_dtrace_provdir)) == NULL) return; /* failed to open directory; just skip it */ ep = alloca(sizeof (struct dirent) + PATH_MAX + 1); bzero(ep, sizeof (struct dirent) + PATH_MAX + 1); while (readdir_r(dirp, ep, &dp) == 0 && dp != NULL) { if (dp->d_name[0] == '.') continue; /* skip "." and ".." */ if (dfp->df_ents == dfp->df_size) { uint_t size = dfp->df_size ? dfp->df_size * 2 : 16; int *fds = realloc(dfp->df_fds, size * sizeof (int)); if (fds == NULL) break; /* skip the rest of this directory */ dfp->df_fds = fds; dfp->df_size = size; } (void) snprintf(path, sizeof (path), "%s/%s", _dtrace_provdir, dp->d_name); if ((fd = open(path, O_RDONLY)) == -1) continue; /* failed to open driver; just skip it */ if (((prov = malloc(sizeof (dt_provmod_t))) == NULL) || (prov->dp_name = malloc(strlen(dp->d_name) + 1)) == NULL) { free(prov); (void) close(fd); break; } (void) strcpy(prov->dp_name, dp->d_name); prov->dp_next = *provmod; *provmod = prov; dt_dprintf("opened provider %s\n", dp->d_name); dfp->df_fds[dfp->df_ents++] = fd; } (void) closedir(dirp); #else /* !illumos */ char *p; char *p1; char *p_providers = NULL; int error; size_t len = 0; /* * Loop to allocate/reallocate memory for the string of provider * names and retry: */ while(1) { /* * The first time around, get the string length. The next time, * hopefully we've allocated enough memory. */ error = sysctlbyname("debug.dtrace.providers",p_providers,&len,NULL,0); if (len == 0) /* No providers? That's strange. Where's dtrace? */ break; else if (error == 0 && p_providers == NULL) { /* * Allocate the initial memory which should be enough * unless another provider loads before we have * time to go back and get the string. */ if ((p_providers = malloc(len)) == NULL) /* How do we report errors here? */ return; } else if (error == -1 && errno == ENOMEM) { /* * The current buffer isn't large enough, so * reallocate it. We normally won't need to do this * because providers aren't being loaded all the time. */ if ((p = realloc(p_providers,len)) == NULL) /* How do we report errors here? */ return; p_providers = p; } else break; } /* Check if we got a string of provider names: */ if (error == 0 && len > 0 && p_providers != NULL) { p = p_providers; /* * Parse the string containing the space separated * provider names. */ while ((p1 = strsep(&p," ")) != NULL) { if (dfp->df_ents == dfp->df_size) { uint_t size = dfp->df_size ? dfp->df_size * 2 : 16; int *fds = realloc(dfp->df_fds, size * sizeof (int)); if (fds == NULL) break; dfp->df_fds = fds; dfp->df_size = size; } (void) snprintf(path, sizeof (path), "/dev/dtrace/%s", p1); if ((fd = open(path, O_RDONLY)) == -1) continue; /* failed to open driver; just skip it */ if (((prov = malloc(sizeof (dt_provmod_t))) == NULL) || (prov->dp_name = malloc(strlen(p1) + 1)) == NULL) { free(prov); (void) close(fd); break; } (void) strcpy(prov->dp_name, p1); prov->dp_next = *provmod; *provmod = prov; dt_dprintf("opened provider %s\n", p1); dfp->df_fds[dfp->df_ents++] = fd; } } if (p_providers != NULL) free(p_providers); #endif /* illumos */ } static void dt_provmod_destroy(dt_provmod_t **provmod) { dt_provmod_t *next, *current; for (current = *provmod; current != NULL; current = next) { next = current->dp_next; free(current->dp_name); free(current); } *provmod = NULL; } #ifdef illumos static const char * dt_get_sysinfo(int cmd, char *buf, size_t len) { ssize_t rv = sysinfo(cmd, buf, len); char *p = buf; if (rv < 0 || rv > len) (void) snprintf(buf, len, "%s", "Unknown"); while ((p = strchr(p, '.')) != NULL) *p++ = '_'; return (buf); } #endif static dtrace_hdl_t * dt_vopen(int version, int flags, int *errp, const dtrace_vector_t *vector, void *arg) { dtrace_hdl_t *dtp = NULL; int dtfd = -1, ftfd = -1, fterr = 0; dtrace_prog_t *pgp; dt_module_t *dmp; dt_provmod_t *provmod = NULL; int i, err; struct rlimit rl; const dt_intrinsic_t *dinp; const dt_typedef_t *dtyp; const dt_ident_t *idp; dtrace_typeinfo_t dtt; ctf_funcinfo_t ctc; ctf_arinfo_t ctr; dt_fdlist_t df = { NULL, 0, 0 }; char isadef[32], utsdef[32]; char s1[64], s2[64]; if (version <= 0) return (set_open_errno(dtp, errp, EINVAL)); if (version > DTRACE_VERSION) return (set_open_errno(dtp, errp, EDT_VERSION)); if (version < DTRACE_VERSION) { /* * Currently, increasing the library version number is used to * denote a binary incompatible change. That is, a consumer * of the library cannot run on a version of the library with * a higher DTRACE_VERSION number than the consumer compiled * against. Once the library API has been committed to, * backwards binary compatibility will be required; at that * time, this check should change to return EDT_OVERSION only * if the specified version number is less than the version * number at the time of interface commitment. */ return (set_open_errno(dtp, errp, EDT_OVERSION)); } if (flags & ~DTRACE_O_MASK) return (set_open_errno(dtp, errp, EINVAL)); if ((flags & DTRACE_O_LP64) && (flags & DTRACE_O_ILP32)) return (set_open_errno(dtp, errp, EINVAL)); if (vector == NULL && arg != NULL) return (set_open_errno(dtp, errp, EINVAL)); if (elf_version(EV_CURRENT) == EV_NONE) return (set_open_errno(dtp, errp, EDT_ELFVERSION)); if (vector != NULL || (flags & DTRACE_O_NODEV)) goto alloc; /* do not attempt to open dtrace device */ /* * Before we get going, crank our limit on file descriptors up to the * hard limit. This is to allow for the fact that libproc keeps file * descriptors to objects open for the lifetime of the proc handle; * without raising our hard limit, we would have an acceptably small * bound on the number of processes that we could concurrently * instrument with the pid provider. */ if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { rl.rlim_cur = rl.rlim_max; (void) setrlimit(RLIMIT_NOFILE, &rl); } /* * Get the device path of each of the providers. We hold them open * in the df.df_fds list until we open the DTrace driver itself, * allowing us to see all of the probes provided on this system. Once * we have the DTrace driver open, we can safely close all the providers * now that they have registered with the framework. */ dt_provmod_open(&provmod, &df); dtfd = open("/dev/dtrace/dtrace", O_RDWR); err = errno; /* save errno from opening dtfd */ #if defined(__FreeBSD__) /* * Automatically load the 'dtraceall' module if we couldn't open the * char device. */ if (err == ENOENT && modfind("dtraceall") < 0) { kldload("dtraceall"); /* ignore the error */ dtfd = open("/dev/dtrace/dtrace", O_RDWR); err = errno; } #endif #ifdef illumos ftfd = open("/dev/dtrace/provider/fasttrap", O_RDWR); #else ftfd = open("/dev/dtrace/fasttrap", O_RDWR); #endif fterr = ftfd == -1 ? errno : 0; /* save errno from open ftfd */ while (df.df_ents-- != 0) (void) close(df.df_fds[df.df_ents]); free(df.df_fds); /* * If we failed to open the dtrace device, fail dtrace_open(). * We convert some kernel errnos to custom libdtrace errnos to * improve the resulting message from the usual strerror(). */ if (dtfd == -1) { dt_provmod_destroy(&provmod); switch (err) { case ENOENT: err = EDT_NOENT; break; case EBUSY: err = EDT_BUSY; break; case EACCES: err = EDT_ACCESS; break; } return (set_open_errno(dtp, errp, err)); } (void) fcntl(dtfd, F_SETFD, FD_CLOEXEC); (void) fcntl(ftfd, F_SETFD, FD_CLOEXEC); alloc: if ((dtp = malloc(sizeof (dtrace_hdl_t))) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); bzero(dtp, sizeof (dtrace_hdl_t)); dtp->dt_oflags = flags; #ifdef illumos dtp->dt_prcmode = DT_PROC_STOP_PREINIT; #else dtp->dt_prcmode = DT_PROC_STOP_POSTINIT; #endif dtp->dt_linkmode = DT_LINK_KERNEL; dtp->dt_linktype = DT_LTYP_ELF; dtp->dt_xlatemode = DT_XL_STATIC; dtp->dt_stdcmode = DT_STDC_XA; dtp->dt_encoding = DT_ENCODING_UNSET; dtp->dt_version = version; dtp->dt_fd = dtfd; dtp->dt_ftfd = ftfd; dtp->dt_fterr = fterr; dtp->dt_cdefs_fd = -1; dtp->dt_ddefs_fd = -1; #ifdef illumos dtp->dt_stdout_fd = -1; #else dtp->dt_freopen_fp = NULL; #endif dtp->dt_modbuckets = _dtrace_strbuckets; dtp->dt_mods = calloc(dtp->dt_modbuckets, sizeof (dt_module_t *)); +#ifdef __FreeBSD__ + dtp->dt_kmods = calloc(dtp->dt_modbuckets, sizeof (dt_module_t *)); +#endif dtp->dt_provbuckets = _dtrace_strbuckets; dtp->dt_provs = calloc(dtp->dt_provbuckets, sizeof (dt_provider_t *)); dt_proc_hash_create(dtp); dtp->dt_vmax = DT_VERS_LATEST; dtp->dt_cpp_path = strdup(_dtrace_defcpp); dtp->dt_cpp_argv = malloc(sizeof (char *)); dtp->dt_cpp_argc = 1; dtp->dt_cpp_args = 1; dtp->dt_ld_path = strdup(_dtrace_defld); #ifdef __FreeBSD__ dtp->dt_objcopy_path = strdup(_dtrace_defobjcopy); #endif dtp->dt_provmod = provmod; dtp->dt_vector = vector; dtp->dt_varg = arg; dt_dof_init(dtp); (void) uname(&dtp->dt_uts); if (dtp->dt_mods == NULL || dtp->dt_provs == NULL || dtp->dt_procs == NULL || dtp->dt_ld_path == NULL || #ifdef __FreeBSD__ + dtp->dt_kmods == NULL || dtp->dt_objcopy_path == NULL || #endif dtp->dt_cpp_path == NULL || dtp->dt_cpp_argv == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); for (i = 0; i < DTRACEOPT_MAX; i++) dtp->dt_options[i] = DTRACEOPT_UNSET; dtp->dt_cpp_argv[0] = (char *)strbasename(dtp->dt_cpp_path); #ifdef illumos (void) snprintf(isadef, sizeof (isadef), "-D__SUNW_D_%u", (uint_t)(sizeof (void *) * NBBY)); (void) snprintf(utsdef, sizeof (utsdef), "-D__%s_%s", dt_get_sysinfo(SI_SYSNAME, s1, sizeof (s1)), dt_get_sysinfo(SI_RELEASE, s2, sizeof (s2))); if (dt_cpp_add_arg(dtp, "-D__sun") == NULL || dt_cpp_add_arg(dtp, "-D__unix") == NULL || dt_cpp_add_arg(dtp, "-D__SVR4") == NULL || dt_cpp_add_arg(dtp, "-D__SUNW_D=1") == NULL || dt_cpp_add_arg(dtp, isadef) == NULL || dt_cpp_add_arg(dtp, utsdef) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); #endif if (flags & DTRACE_O_NODEV) bcopy(&_dtrace_conf, &dtp->dt_conf, sizeof (_dtrace_conf)); else if (dt_ioctl(dtp, DTRACEIOC_CONF, &dtp->dt_conf) != 0) return (set_open_errno(dtp, errp, errno)); if (flags & DTRACE_O_LP64) dtp->dt_conf.dtc_ctfmodel = CTF_MODEL_LP64; else if (flags & DTRACE_O_ILP32) dtp->dt_conf.dtc_ctfmodel = CTF_MODEL_ILP32; #ifdef __sparc /* * On SPARC systems, __sparc is always defined for * and __sparcv9 is defined if we are doing a 64-bit compile. */ if (dt_cpp_add_arg(dtp, "-D__sparc") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64 && dt_cpp_add_arg(dtp, "-D__sparcv9") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); #endif #ifdef illumos #ifdef __x86 /* * On x86 systems, __i386 is defined for for 32-bit * compiles and __amd64 is defined for 64-bit compiles. Unlike SPARC, * they are defined exclusive of one another (see PSARC 2004/619). */ if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) { if (dt_cpp_add_arg(dtp, "-D__amd64") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } else { if (dt_cpp_add_arg(dtp, "-D__i386") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } #endif #else #if defined(__amd64__) || defined(__i386__) if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) { if (dt_cpp_add_arg(dtp, "-m64") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } else { if (dt_cpp_add_arg(dtp, "-m32") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } #endif #endif if (dtp->dt_conf.dtc_difversion < DIF_VERSION) return (set_open_errno(dtp, errp, EDT_DIFVERS)); if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_ILP32) bcopy(_dtrace_ints_32, dtp->dt_ints, sizeof (_dtrace_ints_32)); else bcopy(_dtrace_ints_64, dtp->dt_ints, sizeof (_dtrace_ints_64)); /* * On FreeBSD the kernel module name can't be hard-coded. The * 'kern.bootfile' sysctl value tells us exactly which file is being * used as the kernel. */ #ifndef illumos { char bootfile[MAXPATHLEN]; char *p; int i; size_t len = sizeof(bootfile); /* This call shouldn't fail, but use a default just in case. */ if (sysctlbyname("kern.bootfile", bootfile, &len, NULL, 0) != 0) strlcpy(bootfile, "kernel", sizeof(bootfile)); if ((p = strrchr(bootfile, '/')) != NULL) p++; else p = bootfile; /* * Format the global variables based on the kernel module name. */ snprintf(curthread_str, sizeof(curthread_str), "%s`struct thread *",p); snprintf(intmtx_str, sizeof(intmtx_str), "int(%s`struct mtx *)",p); snprintf(threadmtx_str, sizeof(threadmtx_str), "struct thread *(%s`struct mtx *)",p); snprintf(rwlock_str, sizeof(rwlock_str), "int(%s`struct rwlock *)",p); snprintf(sxlock_str, sizeof(sxlock_str), "int(%s`struct sxlock *)",p); } #endif dtp->dt_macros = dt_idhash_create("macro", NULL, 0, UINT_MAX); dtp->dt_aggs = dt_idhash_create("aggregation", NULL, DTRACE_AGGVARIDNONE + 1, UINT_MAX); dtp->dt_globals = dt_idhash_create("global", _dtrace_globals, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); dtp->dt_tls = dt_idhash_create("thread local", NULL, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); if (dtp->dt_macros == NULL || dtp->dt_aggs == NULL || dtp->dt_globals == NULL || dtp->dt_tls == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); /* * Populate the dt_macros identifier hash table by hand: we can't use * the dt_idhash_populate() mechanism because we're not yet compiling * and dtrace_update() needs to immediately reference these idents. */ for (idp = _dtrace_macros; idp->di_name != NULL; idp++) { if (dt_idhash_insert(dtp->dt_macros, idp->di_name, idp->di_kind, idp->di_flags, idp->di_id, idp->di_attr, idp->di_vers, idp->di_ops ? idp->di_ops : &dt_idops_thaw, idp->di_iarg, 0) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } /* * Update the module list using /system/object and load the values for * the macro variable definitions according to the current process. */ dtrace_update(dtp); /* * Select the intrinsics and typedefs we want based on the data model. * The intrinsics are under "C". The typedefs are added under "D". */ if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_ILP32) { dinp = _dtrace_intrinsics_32; dtyp = _dtrace_typedefs_32; } else { dinp = _dtrace_intrinsics_64; dtyp = _dtrace_typedefs_64; } /* * Create a dynamic CTF container under the "C" scope for intrinsic * types and types defined in ANSI-C header files that are included. */ if ((dmp = dtp->dt_cdefs = dt_module_create(dtp, "C")) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if ((dmp->dm_ctfp = ctf_create(&dtp->dt_ctferr)) == NULL) return (set_open_errno(dtp, errp, EDT_CTF)); dt_dprintf("created CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); (void) ctf_setmodel(dmp->dm_ctfp, dtp->dt_conf.dtc_ctfmodel); ctf_setspecific(dmp->dm_ctfp, dmp); dmp->dm_flags = DT_DM_LOADED; /* fake up loaded bit */ dmp->dm_modid = -1; /* no module ID */ /* * Fill the dynamic "C" CTF container with all of the intrinsic * integer and floating-point types appropriate for this data model. */ for (; dinp->din_name != NULL; dinp++) { if (dinp->din_kind == CTF_K_INTEGER) { err = ctf_add_integer(dmp->dm_ctfp, CTF_ADD_ROOT, dinp->din_name, &dinp->din_data); } else { err = ctf_add_float(dmp->dm_ctfp, CTF_ADD_ROOT, dinp->din_name, &dinp->din_data); } if (err == CTF_ERR) { dt_dprintf("failed to add %s to C container: %s\n", dinp->din_name, ctf_errmsg( ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } } if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed to update C container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Add intrinsic pointer types that are needed to initialize printf * format dictionary types (see table in dt_printf.c). */ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "void")); (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "char")); (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "int")); if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed to update C container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Create a dynamic CTF container under the "D" scope for types that * are defined by the D program itself or on-the-fly by the D compiler. * The "D" CTF container is a child of the "C" CTF container. */ if ((dmp = dtp->dt_ddefs = dt_module_create(dtp, "D")) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if ((dmp->dm_ctfp = ctf_create(&dtp->dt_ctferr)) == NULL) return (set_open_errno(dtp, errp, EDT_CTF)); dt_dprintf("created CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); (void) ctf_setmodel(dmp->dm_ctfp, dtp->dt_conf.dtc_ctfmodel); ctf_setspecific(dmp->dm_ctfp, dmp); dmp->dm_flags = DT_DM_LOADED; /* fake up loaded bit */ dmp->dm_modid = -1; /* no module ID */ if (ctf_import(dmp->dm_ctfp, dtp->dt_cdefs->dm_ctfp) == CTF_ERR) { dt_dprintf("failed to import D parent container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Fill the dynamic "D" CTF container with all of the built-in typedefs * that we need to use for our D variable and function definitions. * This ensures that basic inttypes.h names are always available to us. */ for (; dtyp->dty_src != NULL; dtyp++) { if (ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, dtyp->dty_dst, ctf_lookup_by_name(dmp->dm_ctfp, dtyp->dty_src)) == CTF_ERR) { dt_dprintf("failed to add typedef %s %s to D " "container: %s", dtyp->dty_src, dtyp->dty_dst, ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } } /* * Insert a CTF ID corresponding to a pointer to a type of kind * CTF_K_FUNCTION we can use in the compiler for function pointers. * CTF treats all function pointers as "int (*)()" so we only need one. */ ctc.ctc_return = ctf_lookup_by_name(dmp->dm_ctfp, "int"); ctc.ctc_argc = 0; ctc.ctc_flags = 0; dtp->dt_type_func = ctf_add_function(dmp->dm_ctfp, CTF_ADD_ROOT, &ctc, NULL); dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, dtp->dt_type_func); /* * We also insert CTF definitions for the special D intrinsic types * string and into the D container. The string type is added * as a typedef of char[n]. The type is an alias for void. * We compare types to these special CTF ids throughout the compiler. */ ctr.ctr_contents = ctf_lookup_by_name(dmp->dm_ctfp, "char"); ctr.ctr_index = ctf_lookup_by_name(dmp->dm_ctfp, "long"); ctr.ctr_nelems = _dtrace_strsize; dtp->dt_type_str = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "string", ctf_add_array(dmp->dm_ctfp, CTF_ADD_ROOT, &ctr)); dtp->dt_type_dyn = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_stack = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "stack", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_symaddr = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "_symaddr", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_usymaddr = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "_usymaddr", ctf_lookup_by_name(dmp->dm_ctfp, "void")); if (dtp->dt_type_func == CTF_ERR || dtp->dt_type_fptr == CTF_ERR || dtp->dt_type_str == CTF_ERR || dtp->dt_type_dyn == CTF_ERR || dtp->dt_type_stack == CTF_ERR || dtp->dt_type_symaddr == CTF_ERR || dtp->dt_type_usymaddr == CTF_ERR) { dt_dprintf("failed to add intrinsic to D container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed update D container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Initialize the integer description table used to convert integer * constants to the appropriate types. Refer to the comments above * dt_node_int() for a complete description of how this table is used. */ for (i = 0; i < sizeof (dtp->dt_ints) / sizeof (dtp->dt_ints[0]); i++) { if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, dtp->dt_ints[i].did_name, &dtt) != 0) { dt_dprintf("failed to lookup integer type %s: %s\n", dtp->dt_ints[i].did_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); return (set_open_errno(dtp, errp, dtp->dt_errno)); } dtp->dt_ints[i].did_ctfp = dtt.dtt_ctfp; dtp->dt_ints[i].did_type = dtt.dtt_type; } /* * Now that we've created the "C" and "D" containers, move them to the * start of the module list so that these types and symbols are found * first (for stability) when iterating through the module list. */ dt_list_delete(&dtp->dt_modlist, dtp->dt_ddefs); dt_list_prepend(&dtp->dt_modlist, dtp->dt_ddefs); dt_list_delete(&dtp->dt_modlist, dtp->dt_cdefs); dt_list_prepend(&dtp->dt_modlist, dtp->dt_cdefs); if (dt_pfdict_create(dtp) == -1) return (set_open_errno(dtp, errp, dtp->dt_errno)); /* * If we are opening libdtrace DTRACE_O_NODEV enable C_ZDEFS by default * because without /dev/dtrace open, we will not be able to load the * names and attributes of any providers or probes from the kernel. */ if (flags & DTRACE_O_NODEV) dtp->dt_cflags |= DTRACE_C_ZDEFS; /* * Load hard-wired inlines into the definition cache by calling the * compiler on the raw definition string defined above. */ if ((pgp = dtrace_program_strcompile(dtp, _dtrace_hardwire, DTRACE_PROBESPEC_NONE, DTRACE_C_EMPTY, 0, NULL)) == NULL) { dt_dprintf("failed to load hard-wired definitions: %s\n", dtrace_errmsg(dtp, dtrace_errno(dtp))); return (set_open_errno(dtp, errp, EDT_HARDWIRE)); } dt_program_destroy(dtp, pgp); /* * Set up the default DTrace library path. Once set, the next call to * dt_compile() will compile all the libraries. We intentionally defer * library processing to improve overhead for clients that don't ever * compile, and to provide better error reporting (because the full * reporting of compiler errors requires dtrace_open() to succeed). */ #ifdef __FreeBSD__ #ifdef __LP64__ if ((dtp->dt_oflags & DTRACE_O_ILP32) != 0) { if (dtrace_setopt(dtp, "libdir", _dtrace_libdir32) != 0) return (set_open_errno(dtp, errp, dtp->dt_errno)); } #endif if (dtrace_setopt(dtp, "libdir", _dtrace_libdir) != 0) return (set_open_errno(dtp, errp, dtp->dt_errno)); #else if (dtrace_setopt(dtp, "libdir", _dtrace_libdir) != 0) return (set_open_errno(dtp, errp, dtp->dt_errno)); #endif return (dtp); } dtrace_hdl_t * dtrace_open(int version, int flags, int *errp) { return (dt_vopen(version, flags, errp, NULL, NULL)); } dtrace_hdl_t * dtrace_vopen(int version, int flags, int *errp, const dtrace_vector_t *vector, void *arg) { return (dt_vopen(version, flags, errp, vector, arg)); } void dtrace_close(dtrace_hdl_t *dtp) { dt_ident_t *idp, *ndp; dt_module_t *dmp; dt_provider_t *pvp; dtrace_prog_t *pgp; dt_xlator_t *dxp; dt_dirpath_t *dirp; +#ifdef __FreeBSD__ + dt_kmodule_t *dkm; + uint_t h; +#endif int i; if (dtp->dt_procs != NULL) dt_proc_hash_destroy(dtp); while ((pgp = dt_list_next(&dtp->dt_programs)) != NULL) dt_program_destroy(dtp, pgp); while ((dxp = dt_list_next(&dtp->dt_xlators)) != NULL) dt_xlator_destroy(dtp, dxp); dt_free(dtp, dtp->dt_xlatormap); for (idp = dtp->dt_externs; idp != NULL; idp = ndp) { ndp = idp->di_next; dt_ident_destroy(idp); } if (dtp->dt_macros != NULL) dt_idhash_destroy(dtp->dt_macros); if (dtp->dt_aggs != NULL) dt_idhash_destroy(dtp->dt_aggs); if (dtp->dt_globals != NULL) dt_idhash_destroy(dtp->dt_globals); if (dtp->dt_tls != NULL) dt_idhash_destroy(dtp->dt_tls); +#ifdef __FreeBSD__ + for (h = 0; h < dtp->dt_modbuckets; h++) + while ((dkm = dtp->dt_kmods[h]) != NULL) { + dtp->dt_kmods[h] = dkm->dkm_next; + free(dkm->dkm_name); + free(dkm); + } +#endif + while ((dmp = dt_list_next(&dtp->dt_modlist)) != NULL) dt_module_destroy(dtp, dmp); while ((pvp = dt_list_next(&dtp->dt_provlist)) != NULL) dt_provider_destroy(dtp, pvp); if (dtp->dt_fd != -1) (void) close(dtp->dt_fd); if (dtp->dt_ftfd != -1) (void) close(dtp->dt_ftfd); if (dtp->dt_cdefs_fd != -1) (void) close(dtp->dt_cdefs_fd); if (dtp->dt_ddefs_fd != -1) (void) close(dtp->dt_ddefs_fd); #ifdef illumos if (dtp->dt_stdout_fd != -1) (void) close(dtp->dt_stdout_fd); #else if (dtp->dt_freopen_fp != NULL) (void) fclose(dtp->dt_freopen_fp); #endif dt_epid_destroy(dtp); dt_aggid_destroy(dtp); dt_format_destroy(dtp); dt_strdata_destroy(dtp); dt_buffered_destroy(dtp); dt_aggregate_destroy(dtp); dt_pfdict_destroy(dtp); dt_provmod_destroy(&dtp->dt_provmod); dt_dof_fini(dtp); for (i = 1; i < dtp->dt_cpp_argc; i++) free(dtp->dt_cpp_argv[i]); while ((dirp = dt_list_next(&dtp->dt_lib_path)) != NULL) { dt_list_delete(&dtp->dt_lib_path, dirp); free(dirp->dir_path); free(dirp); } free(dtp->dt_cpp_argv); free(dtp->dt_cpp_path); free(dtp->dt_ld_path); #ifdef __FreeBSD__ free(dtp->dt_objcopy_path); #endif free(dtp->dt_mods); +#ifdef __FreeBSD__ + free(dtp->dt_kmods); +#endif free(dtp->dt_provs); free(dtp); } int dtrace_provider_modules(dtrace_hdl_t *dtp, const char **mods, int nmods) { dt_provmod_t *prov; int i = 0; for (prov = dtp->dt_provmod; prov != NULL; prov = prov->dp_next, i++) { if (i < nmods) mods[i] = prov->dp_name; } return (i); } int dtrace_ctlfd(dtrace_hdl_t *dtp) { return (dtp->dt_fd); } Index: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pragma.c =================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pragma.c (revision 284084) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pragma.c (revision 284085) @@ -1,556 +1,564 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #ifdef illumos #include #endif #include #include #include #include #include #include #include #include #include #include /* * This callback function is installed in a given identifier hash to search for * and apply deferred pragmas that are pending for a given new identifier name. * Multiple pragmas may be pending for a given name; we processs all of them. */ /*ARGSUSED*/ static void dt_pragma_apply(dt_idhash_t *dhp, dt_ident_t *idp) { dt_idhash_t *php; dt_ident_t *pdp; if ((php = yypcb->pcb_pragmas) == NULL) return; /* no pragmas pending for current compilation pass */ while ((pdp = dt_idhash_lookup(php, idp->di_name)) != NULL) { switch (pdp->di_kind) { case DT_IDENT_PRAGAT: idp->di_attr = pdp->di_attr; break; case DT_IDENT_PRAGBN: idp->di_vers = pdp->di_vers; break; } dt_idhash_delete(php, pdp); } } /* * The #pragma attributes directive can be used to reset stability attributes * on a global identifier or inline definition. If the identifier is already * defined, we can just change di_attr. If not, we insert the pragma into a * hash table of the current pcb's deferred pragmas for later processing. */ static void dt_pragma_attributes(const char *prname, dt_node_t *dnp) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dtrace_attribute_t attr, *a; dt_provider_t *pvp; const char *name, *part; dt_ident_t *idp; if (dnp == NULL || dnp->dn_kind != DT_NODE_IDENT || dnp->dn_list == NULL || dnp->dn_list->dn_kind != DT_NODE_IDENT) { xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s " " \n", prname); } if (dtrace_str2attr(dnp->dn_string, &attr) == -1) { xyerror(D_PRAGMA_INVAL, "invalid attributes " "specified by #pragma %s\n", prname); } dnp = dnp->dn_list; name = dnp->dn_string; if (strcmp(name, "provider") == 0) { dnp = dnp->dn_list; name = dnp->dn_string; dnp = dnp->dn_list; part = dnp->dn_string; if ((pvp = dt_provider_lookup(dtp, name)) != NULL) { if (strcmp(part, "provider") == 0) { a = &pvp->pv_desc.dtvd_attr.dtpa_provider; } else if (strcmp(part, "module") == 0) { a = &pvp->pv_desc.dtvd_attr.dtpa_mod; } else if (strcmp(part, "function") == 0) { a = &pvp->pv_desc.dtvd_attr.dtpa_func; } else if (strcmp(part, "name") == 0) { a = &pvp->pv_desc.dtvd_attr.dtpa_name; } else if (strcmp(part, "args") == 0) { a = &pvp->pv_desc.dtvd_attr.dtpa_args; } else { xyerror(D_PRAGMA_INVAL, "invalid component " "\"%s\" in attribute #pragma " "for provider %s\n", name, part); } *a = attr; return; } } else if ((idp = dt_idstack_lookup( &yypcb->pcb_globals, name)) != NULL) { if (idp->di_gen != dtp->dt_gen) { xyerror(D_PRAGMA_SCOPE, "#pragma %s cannot modify " "entity defined outside program scope\n", prname); } idp->di_attr = attr; return; } if (yypcb->pcb_pragmas == NULL && (yypcb->pcb_pragmas = dt_idhash_create("pragma", NULL, 0, 0)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); idp = dt_idhash_insert(yypcb->pcb_pragmas, name, DT_IDENT_PRAGAT, 0, 0, attr, 0, &dt_idops_thaw, (void *)prname, dtp->dt_gen); if (idp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); if (dtp->dt_globals->dh_defer == NULL) dtp->dt_globals->dh_defer = &dt_pragma_apply; } /* * The #pragma binding directive can be used to reset the version binding * on a global identifier or inline definition. If the identifier is already * defined, we can just change di_vers. If not, we insert the pragma into a * hash table of the current pcb's deferred pragmas for later processing. */ static void dt_pragma_binding(const char *prname, dt_node_t *dnp) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_version_t vers; const char *name; dt_ident_t *idp; if (dnp == NULL || dnp->dn_kind != DT_NODE_STRING || dnp->dn_list == NULL || dnp->dn_list->dn_kind != DT_NODE_IDENT) { xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s " "\"version\" \n", prname); } if (dt_version_str2num(dnp->dn_string, &vers) == -1) { xyerror(D_PRAGMA_INVAL, "invalid version string " "specified by #pragma %s\n", prname); } name = dnp->dn_list->dn_string; idp = dt_idstack_lookup(&yypcb->pcb_globals, name); if (idp != NULL) { if (idp->di_gen != dtp->dt_gen) { xyerror(D_PRAGMA_SCOPE, "#pragma %s cannot modify " "entity defined outside program scope\n", prname); } idp->di_vers = vers; return; } if (yypcb->pcb_pragmas == NULL && (yypcb->pcb_pragmas = dt_idhash_create("pragma", NULL, 0, 0)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); idp = dt_idhash_insert(yypcb->pcb_pragmas, name, DT_IDENT_PRAGBN, 0, 0, _dtrace_defattr, vers, &dt_idops_thaw, (void *)prname, dtp->dt_gen); if (idp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); if (dtp->dt_globals->dh_defer == NULL) dtp->dt_globals->dh_defer = &dt_pragma_apply; } static void dt_pragma_depends_finddep(dtrace_hdl_t *dtp, const char *lname, char *lib, size_t len) { dt_dirpath_t *dirp; struct stat sbuf; int found = 0; for (dirp = dt_list_next(&dtp->dt_lib_path); dirp != NULL; dirp = dt_list_next(dirp)) { (void) snprintf(lib, len, "%s/%s", dirp->dir_path, lname); if (stat(lib, &sbuf) == 0) { found = 1; break; } } if (!found) xyerror(D_PRAGMA_DEPEND, "failed to find dependency in libpath: %s", lname); } /* * The #pragma depends_on directive can be used to express a dependency on a * module, provider or library which if not present will cause processing to * abort. */ static void dt_pragma_depends(const char *prname, dt_node_t *cnp) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *nnp = cnp ? cnp->dn_list : NULL; int found; dt_lib_depend_t *dld; char lib[MAXPATHLEN]; size_t plen; char *provs, *cpy, *tok; if (cnp == NULL || nnp == NULL || cnp->dn_kind != DT_NODE_IDENT || nnp->dn_kind != DT_NODE_IDENT) { xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s " " \n", prname); } if (strcmp(cnp->dn_string, "provider") == 0) { /* * First try to get the provider list using the * debug.dtrace.providers sysctl, since that'll work even if * we're not running as root. */ provs = NULL; if (sysctlbyname("debug.dtrace.providers", NULL, &plen, NULL, 0) || ((provs = dt_alloc(dtp, plen)) == NULL) || sysctlbyname("debug.dtrace.providers", provs, &plen, NULL, 0)) found = dt_provider_lookup(dtp, nnp->dn_string) != NULL; else { found = B_FALSE; for (cpy = provs; (tok = strsep(&cpy, " ")) != NULL; ) if (strcmp(tok, nnp->dn_string) == 0) { found = B_TRUE; break; } if (found == B_FALSE) found = dt_provider_lookup(dtp, nnp->dn_string) != NULL; } if (provs != NULL) dt_free(dtp, provs); } else if (strcmp(cnp->dn_string, "module") == 0) { dt_module_t *mp = dt_module_lookup_by_name(dtp, nnp->dn_string); found = mp != NULL && dt_module_getctf(dtp, mp) != NULL; +#ifdef __FreeBSD__ + if (!found) { + dt_kmodule_t *dkmp = dt_kmodule_lookup(dtp, + nnp->dn_string); + found = dkmp != NULL && + dt_module_getctf(dtp, dkmp->dkm_module) != NULL; + } +#endif } else if (strcmp(cnp->dn_string, "library") == 0) { if (yypcb->pcb_cflags & DTRACE_C_CTL) { assert(dtp->dt_filetag != NULL); dt_pragma_depends_finddep(dtp, nnp->dn_string, lib, sizeof (lib)); dld = dt_lib_depend_lookup(&dtp->dt_lib_dep, dtp->dt_filetag); assert(dld != NULL); if ((dt_lib_depend_add(dtp, &dld->dtld_dependencies, lib)) != 0) { xyerror(D_PRAGMA_DEPEND, "failed to add dependency %s:%s\n", lib, dtrace_errmsg(dtp, dtrace_errno(dtp))); } } else { /* * By this point we have already performed a topological * sort of the dependencies; we process this directive * as satisfied as long as the dependency was properly * loaded. */ if (dtp->dt_filetag == NULL) xyerror(D_PRAGMA_DEPEND, "main program may " "not explicitly depend on a library"); dld = dt_lib_depend_lookup(&dtp->dt_lib_dep, dtp->dt_filetag); assert(dld != NULL); dt_pragma_depends_finddep(dtp, nnp->dn_string, lib, sizeof (lib)); dld = dt_lib_depend_lookup(&dtp->dt_lib_dep_sorted, lib); assert(dld != NULL); if (!dld->dtld_loaded) xyerror(D_PRAGMA_DEPEND, "program requires " "library \"%s\" which failed to load", lib); } found = B_TRUE; } else { xyerror(D_PRAGMA_INVAL, "invalid class %s " "specified by #pragma %s\n", cnp->dn_string, prname); } if (!found) { xyerror(D_PRAGMA_DEPEND, "program requires %s %s\n", cnp->dn_string, nnp->dn_string); } } /* * The #pragma error directive can be followed by any list of tokens, which we * just concatenate and print as part of our error message. */ static void dt_pragma_error(const char *prname, dt_node_t *dnp) { dt_node_t *enp; size_t n = 0; char *s; for (enp = dnp; enp != NULL; enp = enp->dn_list) { if (enp->dn_kind == DT_NODE_IDENT || enp->dn_kind == DT_NODE_STRING) n += strlen(enp->dn_string) + 1; } s = alloca(n + 1); s[0] = '\0'; for (enp = dnp; enp != NULL; enp = enp->dn_list) { if (enp->dn_kind == DT_NODE_IDENT || enp->dn_kind == DT_NODE_STRING) { (void) strcat(s, enp->dn_string); (void) strcat(s, " "); } } xyerror(D_PRAGERR, "#%s: %s\n", prname, s); } /*ARGSUSED*/ static void dt_pragma_ident(const char *prname, dt_node_t *dnp) { /* ignore any #ident or #pragma ident lines */ } static void dt_pragma_option(const char *prname, dt_node_t *dnp) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; char *opt, *val; if (dnp == NULL || dnp->dn_kind != DT_NODE_IDENT) { xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s