sdt.c revision 260817
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: stable/10/sys/cddl/dev/sdt/sdt.c 260817 2014-01-17 10:58:59Z avg $
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. */
50static void	sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
51static void	sdt_provide_probes(void *, dtrace_probedesc_t *);
52static void	sdt_destroy(void *, dtrace_id_t, void *);
53static void	sdt_enable(void *, dtrace_id_t, void *);
54static void	sdt_disable(void *, dtrace_id_t, void *);
55
56static d_open_t	sdt_open;
57static void	sdt_load(void *);
58static int	sdt_unload(void *);
59static void	sdt_create_provider(struct sdt_provider *);
60static void	sdt_create_probe(struct sdt_probe *);
61static void	sdt_kld_load(void *, struct linker_file *);
62static void	sdt_kld_unload_try(void *, struct linker_file *, int *);
63
64static MALLOC_DEFINE(M_SDT, "SDT", "DTrace SDT providers");
65
66static struct cdevsw sdt_cdevsw = {
67	.d_version	= D_VERSION,
68	.d_open		= sdt_open,
69	.d_name		= "sdt",
70};
71
72static 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
80static 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
93static struct cdev	*sdt_cdev;
94
95static TAILQ_HEAD(, sdt_provider) sdt_prov_list;
96
97eventhandler_tag	sdt_kld_load_tag;
98eventhandler_tag	sdt_kld_unload_try_tag;
99
100static void
101sdt_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
129static void
130sdt_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	const char *from;
137	char *to;
138	size_t len;
139
140	TAILQ_FOREACH(prov, &sdt_prov_list, prov_entry)
141		if (strcmp(prov->name, probe->prov->name) == 0)
142			break;
143
144	KASSERT(prov != NULL, ("probe defined without a provider"));
145
146	/* If no module name was specified, use the module filename. */
147	if (*probe->mod == 0) {
148		len = strlcpy(mod, probe->sdtp_lf->filename, sizeof(mod));
149		if (len > 3 && strcmp(mod + len - 3, ".ko") == 0)
150			mod[len - 3] = '\0';
151	} else
152		strlcpy(mod, probe->mod, sizeof(mod));
153
154	/*
155	 * Unfortunately this is necessary because the Solaris DTrace
156	 * code mixes consts and non-consts with casts to override
157	 * the incompatibilies. On FreeBSD, we use strict warnings
158	 * in the C compiler, so we have to respect const vs non-const.
159	 */
160	strlcpy(func, probe->func, sizeof(func));
161
162	from = probe->name;
163	to = name;
164	for (len = 0; len < (sizeof(name) - 1) && *from != '\0';
165	    len++, from++, to++) {
166		if (from[0] == '_' && from[1] == '_') {
167			*to = '-';
168			from++;
169		} else
170			*to = *from;
171	}
172	*to = '\0';
173
174	if (dtrace_probe_lookup(prov->id, mod, func, name) != DTRACE_IDNONE)
175		return;
176
177	TAILQ_INSERT_TAIL(&prov->probe_list, probe, probe_entry);
178
179	(void)dtrace_probe_create(prov->id, mod, func, name, 1, probe);
180}
181
182/* Probes are created through the SDT module load/unload hook. */
183static void
184sdt_provide_probes(void *arg, dtrace_probedesc_t *desc)
185{
186}
187
188static void
189sdt_enable(void *arg __unused, dtrace_id_t id, void *parg)
190{
191	struct sdt_probe *probe = parg;
192
193	probe->id = id;
194	probe->sdtp_lf->nenabled++;
195}
196
197static void
198sdt_disable(void *arg __unused, dtrace_id_t id, void *parg)
199{
200	struct sdt_probe *probe = parg;
201
202	KASSERT(probe->sdtp_lf->nenabled > 0, ("no probes enabled"));
203
204	probe->id = 0;
205	probe->sdtp_lf->nenabled--;
206}
207
208static void
209sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
210{
211	struct sdt_argtype *argtype;
212	struct sdt_probe *probe = parg;
213
214	if (desc->dtargd_ndx < probe->n_args) {
215		TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) {
216			if (desc->dtargd_ndx == argtype->ndx) {
217				desc->dtargd_mapping = desc->dtargd_ndx;
218				strlcpy(desc->dtargd_native, argtype->type,
219				    sizeof(desc->dtargd_native));
220				if (argtype->xtype != NULL)
221					strlcpy(desc->dtargd_xlate,
222					    argtype->xtype,
223					    sizeof(desc->dtargd_xlate));
224				else
225					desc->dtargd_xlate[0] = '\0';
226			}
227		}
228	} else
229		desc->dtargd_ndx = DTRACE_ARGNONE;
230}
231
232static void
233sdt_destroy(void *arg, dtrace_id_t id, void *parg)
234{
235	struct sdt_probe *probe;
236
237	probe = parg;
238	TAILQ_REMOVE(&probe->prov->probe_list, probe, probe_entry);
239}
240
241/*
242 * Called from the kernel linker when a module is loaded, before
243 * dtrace_module_loaded() is called. This is done so that it's possible to
244 * register new providers when modules are loaded. We cannot do this in the
245 * provide_module method since it's called with the provider lock held
246 * and dtrace_register() will try to acquire it again.
247 */
248static void
249sdt_kld_load(void *arg __unused, struct linker_file *lf)
250{
251	struct sdt_provider **prov, **begin, **end;
252	struct sdt_probe **probe, **p_begin, **p_end;
253	struct sdt_argtype **argtype, **a_begin, **a_end;
254
255	if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, NULL))
256		return;
257	for (prov = begin; prov < end; prov++)
258		sdt_create_provider(*prov);
259
260	if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
261	    NULL))
262		return;
263	for (probe = p_begin; probe < p_end; probe++) {
264		(*probe)->sdtp_lf = lf;
265		sdt_create_probe(*probe);
266		TAILQ_INIT(&(*probe)->argtype_list);
267	}
268
269	if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end,
270	    NULL))
271		return;
272	for (argtype = a_begin; argtype < a_end; argtype++) {
273		(*argtype)->probe->n_args++;
274		TAILQ_INSERT_TAIL(&(*argtype)->probe->argtype_list, *argtype,
275		    argtype_entry);
276	}
277}
278
279static void
280sdt_kld_unload_try(void *arg __unused, struct linker_file *lf, int *error __unused)
281{
282	struct sdt_provider *prov, **curr, **begin, **end, *tmp;
283
284	if (*error != 0)
285		/* We already have an error, so don't do anything. */
286		return;
287	else if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, NULL))
288		/* No DTrace providers are declared in this file. */
289		return;
290
291	/*
292	 * Go through all the providers declared in this linker file and
293	 * unregister any that aren't declared in another loaded file.
294	 */
295	for (curr = begin; curr < end; curr++) {
296		TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
297			if (strcmp(prov->name, (*curr)->name) == 0) {
298				if (prov->sdt_refs == 1) {
299					TAILQ_REMOVE(&sdt_prov_list, prov,
300					    prov_entry);
301					dtrace_unregister(prov->id);
302					free(prov->name, M_SDT);
303					free(prov, M_SDT);
304				} else
305					prov->sdt_refs--;
306				break;
307			}
308		}
309	}
310}
311
312static int
313sdt_linker_file_cb(linker_file_t lf, void *arg __unused)
314{
315
316	sdt_kld_load(NULL, lf);
317
318	return (0);
319}
320
321static void
322sdt_load(void *arg __unused)
323{
324
325	TAILQ_INIT(&sdt_prov_list);
326
327	/* Create the /dev/dtrace/sdt entry. */
328	sdt_cdev = make_dev(&sdt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
329	    "dtrace/sdt");
330
331	sdt_probe_func = dtrace_probe;
332
333	sdt_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, sdt_kld_load, NULL,
334	    EVENTHANDLER_PRI_ANY);
335	sdt_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try,
336	    sdt_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY);
337
338	/* Pick up probes from the kernel and already-loaded linker files. */
339	linker_file_foreach(sdt_linker_file_cb, NULL);
340}
341
342static int
343sdt_unload(void *arg __unused)
344{
345	struct sdt_provider *prov, *tmp;
346
347	EVENTHANDLER_DEREGISTER(kld_load, sdt_kld_load_tag);
348	EVENTHANDLER_DEREGISTER(kld_unload_try, sdt_kld_unload_try_tag);
349
350	sdt_probe_func = sdt_probe_stub;
351
352	TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
353		TAILQ_REMOVE(&sdt_prov_list, prov, prov_entry);
354		dtrace_unregister(prov->id);
355		free(prov->name, M_SDT);
356		free(prov, M_SDT);
357	}
358
359	destroy_dev(sdt_cdev);
360
361	return (0);
362}
363
364/* ARGSUSED */
365static int
366sdt_modevent(module_t mod __unused, int type, void *data __unused)
367{
368	int error = 0;
369
370	switch (type) {
371	case MOD_LOAD:
372		break;
373
374	case MOD_UNLOAD:
375		break;
376
377	case MOD_SHUTDOWN:
378		break;
379
380	default:
381		error = EOPNOTSUPP;
382		break;
383	}
384
385	return (error);
386}
387
388/* ARGSUSED */
389static int
390sdt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused,
391    struct thread *td __unused)
392{
393
394	return (0);
395}
396
397SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL);
398SYSUNINIT(sdt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_unload, NULL);
399
400DEV_MODULE(sdt, sdt_modevent, NULL);
401MODULE_VERSION(sdt, 1);
402MODULE_DEPEND(sdt, dtrace, 1, 1, 1);
403MODULE_DEPEND(sdt, opensolaris, 1, 1, 1);
404