xref: /freebsd/sys/cddl/dev/dtrace/dtrace_load.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * $FreeBSD$
22  *
23  */
24 
25 #ifndef EARLY_AP_STARTUP
26 static void
27 dtrace_ap_start(void *dummy)
28 {
29 	int i;
30 
31 	mutex_enter(&cpu_lock);
32 
33 	/* Setup the rest of the CPUs. */
34 	CPU_FOREACH(i) {
35 		if (i == 0)
36 			continue;
37 
38 		(void) dtrace_cpu_setup(CPU_CONFIG, i);
39 	}
40 
41 	mutex_exit(&cpu_lock);
42 }
43 
44 SYSINIT(dtrace_ap_start, SI_SUB_SMP, SI_ORDER_ANY, dtrace_ap_start, NULL);
45 #endif
46 
47 static void
48 dtrace_load(void *dummy)
49 {
50 	dtrace_provider_id_t id;
51 #ifdef EARLY_AP_STARTUP
52 	int i;
53 #endif
54 
55 #ifndef illumos
56 	/*
57 	 * DTrace uses negative logic for the destructive mode switch, so it
58 	 * is required to translate from the sysctl which uses positive logic.
59 	 */
60 	if (dtrace_allow_destructive)
61 		dtrace_destructive_disallow = 0;
62 	else
63 		dtrace_destructive_disallow = 1;
64 #endif
65 
66 	/* Hook into the trap handler. */
67 	dtrace_trap_func = dtrace_trap;
68 
69 	/* Hang our hook for thread switches. */
70 	dtrace_vtime_switch_func = dtrace_vtime_switch;
71 
72 	/* Hang our hook for exceptions. */
73 	dtrace_invop_init();
74 
75 	dtrace_taskq = taskq_create("dtrace_taskq", 1, maxclsyspri, 0, 0, 0);
76 
77 	dtrace_arena = new_unrhdr(1, INT_MAX, &dtrace_unr_mtx);
78 
79 	/* Register callbacks for linker file load and unload events. */
80 	dtrace_kld_load_tag = EVENTHANDLER_REGISTER(kld_load,
81 	    dtrace_kld_load, NULL, EVENTHANDLER_PRI_ANY);
82 	dtrace_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try,
83 	    dtrace_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY);
84 
85 	/*
86 	 * Initialise the mutexes without 'witness' because the dtrace
87 	 * code is mostly written to wait for memory. To have the
88 	 * witness code change a malloc() from M_WAITOK to M_NOWAIT
89 	 * because a lock is held would surely create a panic in a
90 	 * low memory situation. And that low memory situation might be
91 	 * the very problem we are trying to trace.
92 	 */
93 	mutex_init(&dtrace_lock,"dtrace probe state", MUTEX_DEFAULT, NULL);
94 	mutex_init(&dtrace_provider_lock,"dtrace provider state", MUTEX_DEFAULT, NULL);
95 	mutex_init(&dtrace_meta_lock,"dtrace meta-provider state", MUTEX_DEFAULT, NULL);
96 #ifdef DEBUG
97 	mutex_init(&dtrace_errlock,"dtrace error lock", MUTEX_DEFAULT, NULL);
98 #endif
99 
100 	mutex_enter(&cpu_lock);
101 	mutex_enter(&dtrace_provider_lock);
102 	mutex_enter(&dtrace_lock);
103 
104 	dtrace_state_cache = kmem_cache_create("dtrace_state_cache",
105 	    sizeof (dtrace_dstate_percpu_t) * NCPU, DTRACE_STATE_ALIGN,
106 	    NULL, NULL, NULL, NULL, NULL, 0);
107 
108 	ASSERT(MUTEX_HELD(&cpu_lock));
109 	dtrace_bymod = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_mod),
110 	    offsetof(dtrace_probe_t, dtpr_nextmod),
111 	    offsetof(dtrace_probe_t, dtpr_prevmod));
112 
113 	dtrace_byfunc = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_func),
114 	    offsetof(dtrace_probe_t, dtpr_nextfunc),
115 	    offsetof(dtrace_probe_t, dtpr_prevfunc));
116 
117 	dtrace_byname = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_name),
118 	    offsetof(dtrace_probe_t, dtpr_nextname),
119 	    offsetof(dtrace_probe_t, dtpr_prevname));
120 
121 	if (dtrace_retain_max < 1) {
122 		cmn_err(CE_WARN, "illegal value (%zu) for dtrace_retain_max; "
123 		    "setting to 1", dtrace_retain_max);
124 		dtrace_retain_max = 1;
125 	}
126 
127 	/*
128 	 * Now discover our toxic ranges.
129 	 */
130 	dtrace_toxic_ranges(dtrace_toxrange_add);
131 
132 	/*
133 	 * Before we register ourselves as a provider to our own framework,
134 	 * we would like to assert that dtrace_provider is NULL -- but that's
135 	 * not true if we were loaded as a dependency of a DTrace provider.
136 	 * Once we've registered, we can assert that dtrace_provider is our
137 	 * pseudo provider.
138 	 */
139 	(void) dtrace_register("dtrace", &dtrace_provider_attr,
140 	    DTRACE_PRIV_NONE, 0, &dtrace_provider_ops, NULL, &id);
141 
142 	ASSERT(dtrace_provider != NULL);
143 	ASSERT((dtrace_provider_id_t)dtrace_provider == id);
144 
145 	dtrace_probeid_begin = dtrace_probe_create((dtrace_provider_id_t)
146 	    dtrace_provider, NULL, NULL, "BEGIN", 0, NULL);
147 	dtrace_probeid_end = dtrace_probe_create((dtrace_provider_id_t)
148 	    dtrace_provider, NULL, NULL, "END", 0, NULL);
149 	dtrace_probeid_error = dtrace_probe_create((dtrace_provider_id_t)
150 	    dtrace_provider, NULL, NULL, "ERROR", 1, NULL);
151 
152 	mutex_exit(&dtrace_lock);
153 	mutex_exit(&dtrace_provider_lock);
154 
155 #ifdef EARLY_AP_STARTUP
156 	CPU_FOREACH(i) {
157 		(void) dtrace_cpu_setup(CPU_CONFIG, i);
158 	}
159 #else
160 	/* Setup the boot CPU */
161 	(void) dtrace_cpu_setup(CPU_CONFIG, 0);
162 #endif
163 
164 	mutex_exit(&cpu_lock);
165 
166 	dtrace_dev = make_dev(&dtrace_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
167 	    "dtrace/dtrace");
168 	helper_dev = make_dev(&helper_cdevsw, 0, UID_ROOT, GID_WHEEL, 0660,
169 	    "dtrace/helper");
170 }
171