📄 dtrace.c
字号:
/* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only. * See the file usr/src/LICENSING.NOTICE in this distribution or * http://www.opensolaris.org/license/ for details. */#pragma ident "@(#)dtrace.c 1.28 04/12/18 SMI"/* * DTrace - Dynamic Tracing for Solaris * * This is the implementation of the Solaris Dynamic Tracing framework * (DTrace). The user-visible interface to DTrace is described at length in * the "Solaris Dynamic Tracing Guide". The interfaces between the libdtrace * library, the in-kernel DTrace framework, and the DTrace providers are * described in the block comments in the <sys/dtrace.h> header file. The * internal architecture of DTrace is described in the block comments in the * <sys/dtrace_impl.h> header file. The comments contained within the DTrace * implementation very much assume mastery of all of these sources; if one has * an unanswered question about the implementation, one should consult them * first. * * The functions here are ordered roughly as follows: * * - Probe context functions * - Probe hashing functions * - Non-probe context utility functions * - Matching functions * - Provider-to-Framework API functions * - Probe management functions * - DIF object functions * - Format functions * - Predicate functions * - ECB functions * - Buffer functions * - Enabling functions * - DOF functions * - Anonymous enabling functions * - Consumer state functions * - Helper functions * - Hook functions * - Driver cookbook functions * * Each group of functions begins with a block comment labelled the "DTrace * [Group] Functions", allowing one to find each block by searching forward * on capital-f functions. */#include <sys/errno.h>#include <sys/stat.h>#include <sys/modctl.h>#include <sys/conf.h>#include <sys/systm.h>#include <sys/ddi.h>#include <sys/sunddi.h>#include <sys/cpuvar.h>#include <sys/kmem.h>#include <sys/strsubr.h>#include <sys/sysmacros.h>#include <sys/dtrace_impl.h>#include <sys/atomic.h>#include <sys/cmn_err.h>#include <sys/mutex_impl.h>#include <sys/rwlock_impl.h>#include <sys/ctf_api.h>#include <sys/panic.h>#include <sys/priv_impl.h>#include <sys/policy.h>#include <sys/cred_impl.h>#include <sys/procfs_isa.h>#include <sys/taskq.h>#include <sys/mkdev.h>#include <sys/kdi.h>#include <sys/zone.h>/* * DTrace Tunable Variables * * The following variables may be tuned by adding a line to /etc/system that * includes both the name of the DTrace module ("dtrace") and the name of the * variable. For example: * * set dtrace:dtrace_destructive_disallow = 1 * * In general, the only variables that one should be tuning this way are those * that affect system-wide DTrace behavior, and for which the default behavior * is undesirable. Most of these variables are tunable on a per-consumer * basis using DTrace options, and need not be tuned on a system-wide basis. */int dtrace_destructive_disallow = 0;dtrace_optval_t dtrace_nonroot_maxsize = (16 * 1024 * 1024);size_t dtrace_difo_maxsize = (256 * 1024);dtrace_optval_t dtrace_dof_maxsize = (256 * 1024);size_t dtrace_global_maxsize = (16 * 1024);size_t dtrace_actions_max = (16 * 1024);dtrace_optval_t dtrace_helper_actions_max = 32;dtrace_optval_t dtrace_helper_providers_max = 32;dtrace_optval_t dtrace_dstate_defsize = (1 * 1024 * 1024);size_t dtrace_strsize_default = 256;dtrace_optval_t dtrace_cleanrate_default = 9900990; /* 101 hz */dtrace_optval_t dtrace_cleanrate_min = 200000; /* 5000 hz */dtrace_optval_t dtrace_cleanrate_max = (uint64_t)60 * NANOSEC; /* 1/minute */dtrace_optval_t dtrace_aggrate_default = NANOSEC; /* 1 hz */dtrace_optval_t dtrace_statusrate_default = NANOSEC; /* 1 hz */dtrace_optval_t dtrace_statusrate_max = (hrtime_t)10 * NANOSEC; /* 6/minute */dtrace_optval_t dtrace_switchrate_default = NANOSEC; /* 1 hz */dtrace_optval_t dtrace_nspec_default = 1;dtrace_optval_t dtrace_specsize_default = 32 * 1024;dtrace_optval_t dtrace_stackframes_default = 20;dtrace_optval_t dtrace_ustackframes_default = 20;dtrace_optval_t dtrace_jstackframes_default = 50;dtrace_optval_t dtrace_jstackstrsize_default = 512;int dtrace_msgdsize_max = 128;hrtime_t dtrace_chill_max = 500 * (NANOSEC / MILLISEC); /* 500 ms */hrtime_t dtrace_chill_interval = NANOSEC; /* 1000 ms */int dtrace_devdepth_max = 32;int dtrace_err_verbose;hrtime_t dtrace_deadman_interval = NANOSEC;hrtime_t dtrace_deadman_timeout = (hrtime_t)10 * NANOSEC;hrtime_t dtrace_deadman_user = (hrtime_t)30 * NANOSEC;/* * DTrace External Variables * * As dtrace(7D) is a kernel module, any DTrace variables are obviously * available to DTrace consumers via the backtick (`) syntax. One of these, * dtrace_zero, is made deliberately so: it is provided as a source of * well-known, zero-filled memory. While this variable is not documented, * it is used by some translators as an implementation detail. */const char dtrace_zero[256] = { 0 }; /* zero-filled memory *//* * DTrace Internal Variables */static dev_info_t *dtrace_devi; /* device info */static vmem_t *dtrace_arena; /* probe ID arena */static vmem_t *dtrace_minor; /* minor number arena */static taskq_t *dtrace_taskq; /* task queue */static dtrace_probe_t **dtrace_probes; /* array of all probes */static int dtrace_nprobes; /* number of probes */static dtrace_provider_t *dtrace_provider; /* provider list */static dtrace_meta_t *dtrace_meta_pid; /* user-land meta provider */static int dtrace_opens; /* number of opens */static void *dtrace_softstate; /* softstate pointer */static dtrace_hash_t *dtrace_bymod; /* probes hashed by module */static dtrace_hash_t *dtrace_byfunc; /* probes hashed by function */static dtrace_hash_t *dtrace_byname; /* probes hashed by name */static dtrace_toxrange_t *dtrace_toxrange; /* toxic range array */static int dtrace_toxranges; /* number of toxic ranges */static int dtrace_toxranges_max; /* size of toxic range array */static dtrace_anon_t dtrace_anon; /* anonymous enabling */static kmem_cache_t *dtrace_state_cache; /* cache for dynamic state */static uint64_t dtrace_vtime_references; /* number of vtimestamp refs */static kthread_t *dtrace_panicked; /* panicking thread */static dtrace_ecb_t *dtrace_ecb_create_cache; /* cached created ECB */static int dtrace_double_errors; /* ERRORs inducing error */static dtrace_state_t *dtrace_state; /* temporary variable */static int dtrace_error; /* temporary variable *//* * List of helpers that must be processed once a 'pid' meta provider comes * along. This is protected by dtrace_lock. */static dtrace_helpers_t *dtrace_deferred_pid;/* * DTrace Locking * DTrace is protected by three (relatively coarse-grained) locks: * * (1) dtrace_lock is required to manipulate essentially any DTrace state, * including enabling state, probes, ECBs, consumer state, helper state, * etc. Importantly, dtrace_lock is _not_ required when in probe context; * probe context is lock-free -- synchronization is handled via the * dtrace_sync() cross call mechanism. * * (2) dtrace_provider_lock is required when manipulating provider state, or * when provider state must be held constant. * * (3) dtrace_meta_lock is required when manipulating meta provider state, or * when meta provider state must be held constant. * * The lock ordering between these three locks is dtrace_meta_lock before * dtrace_provider_lock before dtrace_lock. (In particular, there are * several places where dtrace_provider_lock is held by the framework as it * calls into the providers -- which then call back into the framework, * grabbing dtrace_lock.) * * There are two other locks in the mix: mod_lock and cpu_lock. cpu_lock * continues its historical role as a coarse-grained lock; it is acquired * before both dtrace_provider_lock and dtrace_lock. mod_lock is slightly * stranger: it must be acquired _between_ dtrace_provider_lock and * dtrace_lock. */static kmutex_t dtrace_lock; /* probe state lock */static kmutex_t dtrace_provider_lock; /* provider state lock */static kmutex_t dtrace_meta_lock; /* meta-provider state lock *//* * DTrace Provider Variables * * These are the variables relating to DTrace as a provider (that is, the * provider of the BEGIN, END, and ERROR probes). */static dtrace_pattr_t dtrace_provider_attr = {{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },};static voiddtrace_nullop(void){}static dtrace_pops_t dtrace_provider_ops = { (void (*)(void *, const dtrace_probedesc_t *))dtrace_nullop, (void (*)(void *, struct modctl *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, NULL, NULL, NULL, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop};static dtrace_id_t dtrace_probeid_begin; /* special BEGIN probe */static dtrace_id_t dtrace_probeid_end; /* special END probe */dtrace_id_t dtrace_probeid_error; /* special ERROR probe *//* * DTrace Helper Tracing Variables */uint32_t dtrace_helptrace_next = 0;uint32_t dtrace_helptrace_nlocals;char *dtrace_helptrace_buffer;int dtrace_helptrace_bufsize = 512 * 1024;#ifdef DEBUGint dtrace_helptrace_enabled = 1;#elseint dtrace_helptrace_enabled = 0;#endif/* * DTrace Error Hashing * * On DEBUG kernels, DTrace will track the errors that has seen in a hash * table. This is very useful for checking coverage of tests that are * expected to induce DIF or DOF processing errors, and may be useful for * debugging problems in the DIF code generator or in DOF generation . The * error hash may be examined with the ::dtrace_errhash MDB dcmd. */#ifdef DEBUGstatic dtrace_errhash_t dtrace_errhash[DTRACE_ERRHASHSZ];static const char *dtrace_errlast;static kthread_t *dtrace_errthread;static kmutex_t dtrace_errlock;#endif/* * DTrace Macros and Constants * * These are various macros that are useful in various spots in the * implementation, along with a few random constants that have no meaning * outside of the implementation. There is no real structure to this cpp * mishmash -- but is there ever? */#define DTRACE_HASHSTR(hash, probe) \ dtrace_hash_str(*((char **)((uintptr_t)(probe) + (hash)->dth_stroffs)))#define DTRACE_HASHNEXT(hash, probe) \ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_nextoffs)#define DTRACE_HASHPREV(hash, probe) \ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_prevoffs)#define DTRACE_HASHEQ(hash, lhs, rhs) \ (strcmp(*((char **)((uintptr_t)(lhs) + (hash)->dth_stroffs)), \ *((char **)((uintptr_t)(rhs) + (hash)->dth_stroffs))) == 0)#define DTRACE_AGGHASHSIZE_SLEW 17/* * The key for a thread-local variable consists of the lower 61 bits of the * t_did, plus the 3 bits of the highest active interrupt above LOCK_LEVEL. * We add DIF_VARIABLE_MAX to t_did to assure that the thread key is never * equal to a variable identifier. This is necessary (but not sufficient) to * assure that global associative arrays never collide with thread-local * variables. To guarantee that they cannot collide, we must also define the * order for keying dynamic variables. That order is: * * [ key0 ] ... [ keyn ] [ variable-key ] [ tls-key ] * * Because the variable-key and the tls-key are in orthogonal spaces, there is * no way for a global variable key signature to match a thread-local key * signature. */#define DTRACE_TLS_THRKEY(where) { \ uint_t intr = 0; \ uint_t actv = CPU->cpu_intr_actv >> (LOCK_LEVEL + 1); \ for (; actv; actv >>= 1) \ intr++; \ ASSERT(intr < (1 << 3)); \ (where) = ((curthread->t_did + DIF_VARIABLE_MAX) & \ (((uint64_t)1 << 61) - 1)) | ((uint64_t)intr << 61); \}#define DTRACE_STORE(type, tomax, offset, what) \ *((type *)((uintptr_t)(tomax) + (uintptr_t)offset)) = (type)(what);#ifndef __i386#define DTRACE_ALIGNCHECK(addr, size, flags) \ if (addr & (size - 1)) { \ *flags |= CPU_DTRACE_BADALIGN; \ cpu_core[CPU->cpu_id].cpuc_dtrace_illval = addr; \ return (0); \ }#else#define DTRACE_ALIGNCHECK(addr, size, flags)#endif#define DTRACE_LOADFUNC(bits) \/*CSTYLED*/ \uint##bits##_t \dtrace_load##bits(uintptr_t addr) \{ \ size_t size = bits / NBBY; \ /*CSTYLED*/ \ uint##bits##_t rval; \ int i; \ volatile uint16_t *flags = (volatile uint16_t *) \ &cpu_core[CPU->cpu_id].cpuc_dtrace_flags; \ \ DTRACE_ALIGNCHECK(addr, size, flags); \ \ for (i = 0; i < dtrace_toxranges; i++) { \ if (addr >= dtrace_toxrange[i].dtt_limit) \ continue; \ \ if (addr + size <= dtrace_toxrange[i].dtt_base) \ continue; \ \ /* \ * This address falls within a toxic region; return 0. \ */ \ *flags |= CPU_DTRACE_BADADDR; \ cpu_core[CPU->cpu_id].cpuc_dtrace_illval = addr; \ return (0); \ } \ \ *flags |= CPU_DTRACE_NOFAULT; \ /*CSTYLED*/ \ rval = *((volatile uint##bits##_t *)addr); \ *flags &= ~CPU_DTRACE_NOFAULT; \ \ return (rval); \}#ifdef _LP64#define dtrace_loadptr dtrace_load64#else#define dtrace_loadptr dtrace_load32#endif#define DTRACE_MATCH_NEXT 0#define DTRACE_MATCH_DONE 1#define DTRACE_ANCHORED(probe) ((probe)->dtpr_func[0] != '\0')#define DTRACE_STATE_ALIGN 64static dtrace_probe_t *dtrace_probe_lookup_id(dtrace_id_t id);static void dtrace_anon_provide(void);static dtrace_state_t *dtrace_anon_grab(void);static void dtrace_anon_match(const char *, dtrace_probespec_t);static uint64_t dtrace_helper(int, dtrace_mstate_t *, dtrace_state_t *, uint64_t, uint64_t);static dtrace_helpers_t *dtrace_helpers_create(proc_t *);static void dtrace_buffer_drop(dtrace_buffer_t *);static intptr_t dtrace_buffer_reserve(dtrace_buffer_t *, size_t, size_t, dtrace_state_t *, dtrace_mstate_t *);static int dtrace_state_option(dtrace_state_t *, dtrace_optid_t, dtrace_optval_t);static int dtrace_ecb_create_enable(dtrace_probe_t *, void *);/* * DTrace Probe Context Functions * * These functions are called from probe context. Because probe context is * any context in which C may be called, arbitrarily locks may be held, * interrupts may be disabled, we may be in arbitrary dispatched state, etc. * As a result, functions called from probe context may only call other DTrace * support functions -- they may not interact at all with the system at large. * (Note that the ASSERT macro is made probe-context safe by redefining it in * terms of dtrace_assfail(), a probe-context safe function.) If arbitrary * loads are to be performed from probe context, they _must_ be in terms of * the safe dtrace_load*() variants. * * Some functions in this block are not actually called from probe context; * for these functions, there will be a comment above the function reading * "Note: not called from probe context." */voiddtrace_panic(const char *format, ...){ va_list alist; va_start(alist, format); dtrace_vpanic(format, alist); va_end(alist);}intdtrace_assfail(const char *a, const char *f, int l){ dtrace_panic("assertion failed: %s, file: %s, line: %d", a, f, l); /* * We just need something here that even the most clever compiler * cannot optimize away. */ return (a[(uintptr_t)f]);}/* * Use the DTRACE_LOADFUNC macro to define functions for each of loading a * uint8_t, a uint16_t, a uint32_t and a uint64_t. */DTRACE_LOADFUNC(8)DTRACE_LOADFUNC(16)DTRACE_LOADFUNC(32)DTRACE_LOADFUNC(64)static intdtrace_inscratch(uintptr_t dest, size_t size, dtrace_mstate_t *mstate){ if (dest < mstate->dtms_scratch_base) return (0); if (dest + size < dest) return (0); if (dest + size > mstate->dtms_scratch_ptr) return (0); return (1);}/* * Check to see if the address is within a memory region to which a store may * be issued. This includes the DTrace scratch areas, and any DTrace variable * region. The caller of dtrace_canstore() is responsible for performing any * alignment checks that are needed before stores are actually executed. */static intdtrace_canstore(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate){ uintptr_t a; size_t s; int i; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -