]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/cddl/dev/sdt/sdt.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / cddl / dev / sdt / sdt.c
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  * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22  *
23  * $FreeBSD$
24  *
25  */
26
27 #include "opt_kdtrace.h"
28
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32
33 #include <sys/conf.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/limits.h>
37 #include <sys/linker.h>
38 #include <sys/linker_set.h>
39 #include <sys/lock.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
43 #include <sys/queue.h>
44 #include <sys/sdt.h>
45
46 #include <sys/dtrace.h>
47 #include <sys/dtrace_bsd.h>
48
49 /* DTrace methods. */
50 static void     sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
51 static void     sdt_provide_probes(void *, dtrace_probedesc_t *);
52 static void     sdt_destroy(void *, dtrace_id_t, void *);
53 static void     sdt_enable(void *, dtrace_id_t, void *);
54 static void     sdt_disable(void *, dtrace_id_t, void *);
55
56 static d_open_t sdt_open;
57 static void     sdt_load(void *);
58 static int      sdt_unload(void *);
59 static void     sdt_create_provider(struct sdt_provider *);
60 static void     sdt_create_probe(struct sdt_probe *);
61 static void     sdt_kld_load(void *, struct linker_file *);
62 static void     sdt_kld_unload_try(void *, struct linker_file *, int *);
63
64 static MALLOC_DEFINE(M_SDT, "SDT", "DTrace SDT providers");
65
66 static struct cdevsw sdt_cdevsw = {
67         .d_version      = D_VERSION,
68         .d_open         = sdt_open,
69         .d_name         = "sdt",
70 };
71
72 static dtrace_pattr_t sdt_attr = {
73 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
74 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
75 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
76 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
77 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
78 };
79
80 static dtrace_pops_t sdt_pops = {
81         sdt_provide_probes,
82         NULL,
83         sdt_enable,
84         sdt_disable,
85         NULL,
86         NULL,
87         sdt_getargdesc,
88         NULL,
89         NULL,
90         sdt_destroy,
91 };
92
93 static struct cdev      *sdt_cdev;
94
95 static TAILQ_HEAD(, sdt_provider) sdt_prov_list;
96
97 eventhandler_tag        sdt_kld_load_tag;
98 eventhandler_tag        sdt_kld_unload_try_tag;
99
100 static void
101 sdt_create_provider(struct sdt_provider *prov)
102 {
103         struct sdt_provider *curr, *newprov;
104
105         TAILQ_FOREACH(curr, &sdt_prov_list, prov_entry)
106                 if (strcmp(prov->name, curr->name) == 0) {
107                         /* The provider has already been defined. */
108                         curr->sdt_refs++;
109                         return;
110                 }
111
112         /*
113          * Make a copy of prov so that we don't lose fields if its module is
114          * unloaded but the provider isn't destroyed. This could happen with
115          * a provider that spans multiple modules.
116          */
117         newprov = malloc(sizeof(*newprov), M_SDT, M_WAITOK | M_ZERO);
118         newprov->name = strdup(prov->name, M_SDT);
119         prov->sdt_refs = newprov->sdt_refs = 1;
120         TAILQ_INIT(&newprov->probe_list);
121
122         TAILQ_INSERT_TAIL(&sdt_prov_list, newprov, prov_entry);
123
124         (void)dtrace_register(newprov->name, &sdt_attr, DTRACE_PRIV_USER, NULL,
125             &sdt_pops, NULL, (dtrace_provider_id_t *)&newprov->id);
126         prov->id = newprov->id;
127 }
128
129 static void
130 sdt_create_probe(struct sdt_probe *probe)
131 {
132         struct sdt_provider *prov;
133         char mod[DTRACE_MODNAMELEN];
134         char func[DTRACE_FUNCNAMELEN];
135         char name[DTRACE_NAMELEN];
136         size_t len;
137
138         TAILQ_FOREACH(prov, &sdt_prov_list, prov_entry)
139                 if (strcmp(prov->name, probe->prov->name) == 0)
140                         break;
141
142         KASSERT(prov != NULL, ("probe defined without a provider"));
143
144         /* If no module name was specified, use the module filename. */
145         if (*probe->mod == 0) {
146                 len = strlcpy(mod, probe->sdtp_lf->filename, sizeof(mod));
147                 if (len > 3 && strcmp(mod + len - 3, ".ko") == 0)
148                         mod[len - 3] = '\0';
149         } else
150                 strlcpy(mod, probe->mod, sizeof(mod));
151
152         /*
153          * Unfortunately this is necessary because the Solaris DTrace
154          * code mixes consts and non-consts with casts to override
155          * the incompatibilies. On FreeBSD, we use strict warnings
156          * in the C compiler, so we have to respect const vs non-const.
157          */
158         strlcpy(func, probe->func, sizeof(func));
159         strlcpy(name, probe->name, sizeof(name));
160
161         if (dtrace_probe_lookup(prov->id, mod, func, name) != DTRACE_IDNONE)
162                 return;
163
164         TAILQ_INSERT_TAIL(&prov->probe_list, probe, probe_entry);
165
166         (void)dtrace_probe_create(prov->id, mod, func, name, 1, probe);
167 }
168
169 /* Probes are created through the SDT module load/unload hook. */
170 static void
171 sdt_provide_probes(void *arg, dtrace_probedesc_t *desc)
172 {
173 }
174
175 static void
176 sdt_enable(void *arg __unused, dtrace_id_t id, void *parg)
177 {
178         struct sdt_probe *probe = parg;
179
180         probe->id = id;
181         probe->sdtp_lf->nenabled++;
182 }
183
184 static void
185 sdt_disable(void *arg __unused, dtrace_id_t id, void *parg)
186 {
187         struct sdt_probe *probe = parg;
188
189         KASSERT(probe->sdtp_lf->nenabled > 0, ("no probes enabled"));
190
191         probe->id = 0;
192         probe->sdtp_lf->nenabled--;
193 }
194
195 static void
196 sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
197 {
198         struct sdt_argtype *argtype;
199         struct sdt_probe *probe = parg;
200
201         if (desc->dtargd_ndx < probe->n_args) {
202                 TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) {
203                         if (desc->dtargd_ndx == argtype->ndx) {
204                                 desc->dtargd_mapping = desc->dtargd_ndx;
205                                 strlcpy(desc->dtargd_native, argtype->type,
206                                     sizeof(desc->dtargd_native));
207                                 if (argtype->xtype != NULL)
208                                         strlcpy(desc->dtargd_xlate,
209                                             argtype->xtype,
210                                             sizeof(desc->dtargd_xlate));
211                                 else
212                                         desc->dtargd_xlate[0] = '\0';
213                         }
214                 }
215         } else
216                 desc->dtargd_ndx = DTRACE_ARGNONE;
217 }
218
219 static void
220 sdt_destroy(void *arg, dtrace_id_t id, void *parg)
221 {
222         struct sdt_probe *probe;
223
224         probe = parg;
225         TAILQ_REMOVE(&probe->prov->probe_list, probe, probe_entry);
226 }
227
228 /*
229  * Called from the kernel linker when a module is loaded, before
230  * dtrace_module_loaded() is called. This is done so that it's possible to
231  * register new providers when modules are loaded. We cannot do this in the
232  * provide_module method since it's called with the provider lock held
233  * and dtrace_register() will try to acquire it again.
234  */
235 static void
236 sdt_kld_load(void *arg __unused, struct linker_file *lf)
237 {
238         struct sdt_provider **prov, **begin, **end;
239         struct sdt_probe **probe, **p_begin, **p_end;
240         struct sdt_argtype **argtype, **a_begin, **a_end;
241
242         if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, NULL))
243                 return;
244         for (prov = begin; prov < end; prov++)
245                 sdt_create_provider(*prov);
246
247         if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
248             NULL))
249                 return;
250         for (probe = p_begin; probe < p_end; probe++) {
251                 (*probe)->sdtp_lf = lf;
252                 sdt_create_probe(*probe);
253                 TAILQ_INIT(&(*probe)->argtype_list);
254         }
255
256         if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end,
257             NULL))
258                 return;
259         for (argtype = a_begin; argtype < a_end; argtype++) {
260                 (*argtype)->probe->n_args++;
261                 TAILQ_INSERT_TAIL(&(*argtype)->probe->argtype_list, *argtype,
262                     argtype_entry);
263         }
264 }
265
266 static void
267 sdt_kld_unload_try(void *arg __unused, struct linker_file *lf, int *error __unused)
268 {
269         struct sdt_provider *prov, **curr, **begin, **end, *tmp;
270
271         if (*error != 0)
272                 /* We already have an error, so don't do anything. */
273                 return;
274         else if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, NULL))
275                 /* No DTrace providers are declared in this file. */
276                 return;
277
278         /*
279          * Go through all the providers declared in this linker file and
280          * unregister any that aren't declared in another loaded file.
281          */
282         for (curr = begin; curr < end; curr++) {
283                 TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
284                         if (strcmp(prov->name, (*curr)->name) == 0) {
285                                 if (prov->sdt_refs == 1) {
286                                         TAILQ_REMOVE(&sdt_prov_list, prov,
287                                             prov_entry);
288                                         dtrace_unregister(prov->id);
289                                         free(prov->name, M_SDT);
290                                         free(prov, M_SDT);
291                                 } else
292                                         prov->sdt_refs--;
293                                 break;
294                         }
295                 }
296         }
297 }
298
299 static int
300 sdt_linker_file_cb(linker_file_t lf, void *arg __unused)
301 {
302
303         sdt_kld_load(NULL, lf);
304
305         return (0);
306 }
307
308 static void
309 sdt_load(void *arg __unused)
310 {
311
312         TAILQ_INIT(&sdt_prov_list);
313
314         /* Create the /dev/dtrace/sdt entry. */
315         sdt_cdev = make_dev(&sdt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
316             "dtrace/sdt");
317
318         sdt_probe_func = dtrace_probe;
319
320         sdt_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, sdt_kld_load, NULL,
321             EVENTHANDLER_PRI_ANY);
322         sdt_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try,
323             sdt_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY);
324
325         /* Pick up probes from the kernel and already-loaded linker files. */
326         linker_file_foreach(sdt_linker_file_cb, NULL);
327 }
328
329 static int
330 sdt_unload(void *arg __unused)
331 {
332         struct sdt_provider *prov, *tmp;
333
334         EVENTHANDLER_DEREGISTER(kld_load, sdt_kld_load_tag);
335         EVENTHANDLER_DEREGISTER(kld_unload_try, sdt_kld_unload_try_tag);
336
337         sdt_probe_func = sdt_probe_stub;
338
339         TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
340                 TAILQ_REMOVE(&sdt_prov_list, prov, prov_entry);
341                 dtrace_unregister(prov->id);
342                 free(prov->name, M_SDT);
343                 free(prov, M_SDT);
344         }
345
346         destroy_dev(sdt_cdev);
347
348         return (0);
349 }
350
351 /* ARGSUSED */
352 static int
353 sdt_modevent(module_t mod __unused, int type, void *data __unused)
354 {
355         int error = 0;
356
357         switch (type) {
358         case MOD_LOAD:
359                 break;
360
361         case MOD_UNLOAD:
362                 break;
363
364         case MOD_SHUTDOWN:
365                 break;
366
367         default:
368                 error = EOPNOTSUPP;
369                 break;
370         }
371
372         return (error);
373 }
374
375 /* ARGSUSED */
376 static int
377 sdt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,
378     struct thread *td __unused)
379 {
380
381         return (0);
382 }
383
384 SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL);
385 SYSUNINIT(sdt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_unload, NULL);
386
387 DEV_MODULE(sdt, sdt_modevent, NULL);
388 MODULE_VERSION(sdt, 1);
389 MODULE_DEPEND(sdt, dtrace, 1, 1, 1);
390 MODULE_DEPEND(sdt, opensolaris, 1, 1, 1);