dt_program.c revision 297077
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
22/*
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011 by Delphix. All rights reserved.
25 */
26
27#include <unistd.h>
28#include <strings.h>
29#include <stdlib.h>
30#include <errno.h>
31#include <assert.h>
32#include <ctype.h>
33#ifdef illumos
34#include <alloca.h>
35#endif
36
37#include <dt_impl.h>
38#include <dt_program.h>
39#include <dt_printf.h>
40#include <dt_provider.h>
41
42dtrace_prog_t *
43dt_program_create(dtrace_hdl_t *dtp)
44{
45	dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t));
46
47	if (pgp != NULL) {
48		dt_list_append(&dtp->dt_programs, pgp);
49	} else {
50		(void) dt_set_errno(dtp, EDT_NOMEM);
51		return (NULL);
52	}
53
54	/*
55	 * By default, programs start with DOF version 1 so that output files
56	 * containing DOF are backward compatible. If a program requires new
57	 * DOF features, the version is increased as needed.
58	 */
59	pgp->dp_dofversion = DOF_VERSION_1;
60
61	return (pgp);
62}
63
64void
65dt_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp)
66{
67	dt_stmt_t *stp, *next;
68	uint_t i;
69
70	for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
71		next = dt_list_next(stp);
72		dtrace_stmt_destroy(dtp, stp->ds_desc);
73		dt_free(dtp, stp);
74	}
75
76	for (i = 0; i < pgp->dp_xrefslen; i++)
77		dt_free(dtp, pgp->dp_xrefs[i]);
78
79	dt_free(dtp, pgp->dp_xrefs);
80	dt_list_delete(&dtp->dt_programs, pgp);
81	dt_free(dtp, pgp);
82}
83
84/*ARGSUSED*/
85void
86dtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
87    dtrace_proginfo_t *pip)
88{
89	dt_stmt_t *stp;
90	dtrace_actdesc_t *ap;
91	dtrace_ecbdesc_t *last = NULL;
92
93	if (pip == NULL)
94		return;
95
96	bzero(pip, sizeof (dtrace_proginfo_t));
97
98	if (dt_list_next(&pgp->dp_stmts) != NULL) {
99		pip->dpi_descattr = _dtrace_maxattr;
100		pip->dpi_stmtattr = _dtrace_maxattr;
101	} else {
102		pip->dpi_descattr = _dtrace_defattr;
103		pip->dpi_stmtattr = _dtrace_defattr;
104	}
105
106	for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) {
107		dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc;
108
109		if (edp == last)
110			continue;
111		last = edp;
112
113		pip->dpi_descattr =
114		    dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr);
115
116		pip->dpi_stmtattr =
117		    dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr);
118
119		/*
120		 * If there aren't any actions, account for the fact that
121		 * recording the epid will generate a record.
122		 */
123		if (edp->dted_action == NULL)
124			pip->dpi_recgens++;
125
126		for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
127			if (ap->dtad_kind == DTRACEACT_SPECULATE) {
128				pip->dpi_speculations++;
129				continue;
130			}
131
132			if (DTRACEACT_ISAGG(ap->dtad_kind)) {
133				pip->dpi_recgens -= ap->dtad_arg;
134				pip->dpi_aggregates++;
135				continue;
136			}
137
138			if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind))
139				continue;
140
141			if (ap->dtad_kind == DTRACEACT_DIFEXPR &&
142			    ap->dtad_difo->dtdo_rtype.dtdt_kind ==
143			    DIF_TYPE_CTF &&
144			    ap->dtad_difo->dtdo_rtype.dtdt_size == 0)
145				continue;
146
147			pip->dpi_recgens++;
148		}
149	}
150}
151
152int
153dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
154    dtrace_proginfo_t *pip)
155{
156	dtrace_enable_io_t args;
157	void *dof;
158	int n, err;
159
160	dtrace_program_info(dtp, pgp, pip);
161
162	if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
163		return (-1);
164
165	args.dof = dof;
166	args.n_matched = 0;
167	n = dt_ioctl(dtp, DTRACEIOC_ENABLE, &args);
168	dtrace_dof_destroy(dtp, dof);
169
170	if (n == -1) {
171		switch (errno) {
172		case EINVAL:
173			err = EDT_DIFINVAL;
174			break;
175		case EFAULT:
176			err = EDT_DIFFAULT;
177			break;
178		case E2BIG:
179			err = EDT_DIFSIZE;
180			break;
181		case EBUSY:
182			err = EDT_ENABLING_ERR;
183			break;
184		default:
185			err = errno;
186		}
187
188		return (dt_set_errno(dtp, err));
189	}
190
191	if (pip != NULL)
192		pip->dpi_matches += args.n_matched;
193
194	return (0);
195}
196
197static void
198dt_ecbdesc_hold(dtrace_ecbdesc_t *edp)
199{
200	edp->dted_refcnt++;
201}
202
203void
204dt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
205{
206	if (--edp->dted_refcnt > 0)
207		return;
208
209	dt_difo_free(dtp, edp->dted_pred.dtpdd_difo);
210	assert(edp->dted_action == NULL);
211	dt_free(dtp, edp);
212}
213
214dtrace_ecbdesc_t *
215dt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
216{
217	dtrace_ecbdesc_t *edp;
218
219	if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) {
220		(void) dt_set_errno(dtp, EDT_NOMEM);
221		return (NULL);
222	}
223
224	edp->dted_probe = *pdp;
225	dt_ecbdesc_hold(edp);
226	return (edp);
227}
228
229dtrace_stmtdesc_t *
230dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
231{
232	dtrace_stmtdesc_t *sdp;
233
234	if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL)
235		return (NULL);
236
237	dt_ecbdesc_hold(edp);
238	sdp->dtsd_ecbdesc = edp;
239	sdp->dtsd_descattr = _dtrace_defattr;
240	sdp->dtsd_stmtattr = _dtrace_defattr;
241
242	return (sdp);
243}
244
245dtrace_actdesc_t *
246dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
247{
248	dtrace_actdesc_t *new;
249	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
250
251	if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL)
252		return (NULL);
253
254	if (sdp->dtsd_action_last != NULL) {
255		assert(sdp->dtsd_action != NULL);
256		assert(sdp->dtsd_action_last->dtad_next == NULL);
257		sdp->dtsd_action_last->dtad_next = new;
258	} else {
259		dtrace_actdesc_t *ap = edp->dted_action;
260
261		assert(sdp->dtsd_action == NULL);
262		sdp->dtsd_action = new;
263
264		while (ap != NULL && ap->dtad_next != NULL)
265			ap = ap->dtad_next;
266
267		if (ap == NULL)
268			edp->dted_action = new;
269		else
270			ap->dtad_next = new;
271	}
272
273	sdp->dtsd_action_last = new;
274	bzero(new, sizeof (dtrace_actdesc_t));
275	new->dtad_uarg = (uintptr_t)sdp;
276
277	return (new);
278}
279
280int
281dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
282{
283	dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t));
284
285	if (stp == NULL)
286		return (-1); /* errno is set for us */
287
288	dt_list_append(&pgp->dp_stmts, stp);
289	stp->ds_desc = sdp;
290
291	return (0);
292}
293
294int
295dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
296    dtrace_stmt_f *func, void *data)
297{
298	dt_stmt_t *stp, *next;
299	int status = 0;
300
301	for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
302		next = dt_list_next(stp);
303		if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
304			break;
305	}
306
307	return (status);
308}
309
310void
311dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
312{
313	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
314
315	/*
316	 * We need to remove any actions that we have on this ECB, and
317	 * remove our hold on the ECB itself.
318	 */
319	if (sdp->dtsd_action != NULL) {
320		dtrace_actdesc_t *last = sdp->dtsd_action_last;
321		dtrace_actdesc_t *ap, *next;
322
323		assert(last != NULL);
324
325		for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
326			if (ap == sdp->dtsd_action)
327				break;
328
329			if (ap->dtad_next == sdp->dtsd_action)
330				break;
331		}
332
333		assert(ap != NULL);
334
335		if (ap == edp->dted_action)
336			edp->dted_action = last->dtad_next;
337		else
338			ap->dtad_next = last->dtad_next;
339
340		/*
341		 * We have now removed our action list from its ECB; we can
342		 * safely destroy the list.
343		 */
344		last->dtad_next = NULL;
345
346		for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
347			assert(ap->dtad_uarg == (uintptr_t)sdp);
348			dt_difo_free(dtp, ap->dtad_difo);
349			next = ap->dtad_next;
350			dt_free(dtp, ap);
351		}
352	}
353
354	if (sdp->dtsd_fmtdata != NULL)
355		dt_printf_destroy(sdp->dtsd_fmtdata);
356	dt_free(dtp, sdp->dtsd_strdata);
357
358	dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
359	dt_free(dtp, sdp);
360}
361
362typedef struct dt_header_info {
363	dtrace_hdl_t *dthi_dtp;	/* consumer handle */
364	FILE *dthi_out;		/* output file */
365	char *dthi_pmname;	/* provider macro name */
366	char *dthi_pfname;	/* provider function name */
367	int dthi_empty;		/* should we generate empty macros */
368} dt_header_info_t;
369
370static void
371dt_header_fmt_macro(char *buf, const char *str)
372{
373	for (;;) {
374		if (islower(*str)) {
375			*buf++ = *str++ + 'A' - 'a';
376		} else if (*str == '-') {
377			*buf++ = '_';
378			str++;
379		} else if (*str == '.') {
380			*buf++ = '_';
381			str++;
382		} else if ((*buf++ = *str++) == '\0') {
383			break;
384		}
385	}
386}
387
388static void
389dt_header_fmt_func(char *buf, const char *str)
390{
391	for (;;) {
392		if (*str == '-') {
393			*buf++ = '_';
394			*buf++ = '_';
395			str++;
396		} else if ((*buf++ = *str++) == '\0') {
397			break;
398		}
399	}
400}
401
402/*ARGSUSED*/
403static int
404dt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
405{
406	dt_header_info_t *infop = data;
407	dtrace_hdl_t *dtp = infop->dthi_dtp;
408	dt_probe_t *prp = idp->di_data;
409	dt_node_t *dnp;
410	char buf[DT_TYPE_NAMELEN];
411	char *fname;
412	const char *p;
413	int i;
414
415	p = prp->pr_name;
416	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
417		p++;
418
419	fname = alloca(strlen(prp->pr_name) + 1 + i);
420	dt_header_fmt_func(fname, prp->pr_name);
421
422	if (fprintf(infop->dthi_out, "extern void __dtrace_%s___%s(",
423	    infop->dthi_pfname, fname) < 0)
424		return (dt_set_errno(dtp, errno));
425
426	for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
427		if (fprintf(infop->dthi_out, "%s",
428		    ctf_type_name(dnp->dn_ctfp, dnp->dn_type,
429		    buf, sizeof (buf))) < 0)
430			return (dt_set_errno(dtp, errno));
431
432		if (i + 1 != prp->pr_nargc &&
433		    fprintf(infop->dthi_out, ", ") < 0)
434			return (dt_set_errno(dtp, errno));
435	}
436
437	if (i == 0 && fprintf(infop->dthi_out, "void") < 0)
438		return (dt_set_errno(dtp, errno));
439
440	if (fprintf(infop->dthi_out, ");\n") < 0)
441		return (dt_set_errno(dtp, errno));
442
443	if (fprintf(infop->dthi_out,
444	    "#ifndef\t__sparc\n"
445	    "extern int __dtraceenabled_%s___%s(void);\n"
446	    "#else\n"
447	    "extern int __dtraceenabled_%s___%s(long);\n"
448	    "#endif\n",
449	    infop->dthi_pfname, fname, infop->dthi_pfname, fname) < 0)
450		return (dt_set_errno(dtp, errno));
451
452	return (0);
453}
454
455/*ARGSUSED*/
456static int
457dt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
458{
459	dt_header_info_t *infop = data;
460	dtrace_hdl_t *dtp = infop->dthi_dtp;
461	dt_probe_t *prp = idp->di_data;
462	char *mname, *fname;
463	const char *p;
464	int i;
465
466	p = prp->pr_name;
467	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
468		p++;
469
470	mname = alloca(strlen(prp->pr_name) + 1);
471	dt_header_fmt_macro(mname, prp->pr_name);
472
473	fname = alloca(strlen(prp->pr_name) + 1 + i);
474	dt_header_fmt_func(fname, prp->pr_name);
475
476	if (fprintf(infop->dthi_out, "#define\t%s_%s(",
477	    infop->dthi_pmname, mname) < 0)
478		return (dt_set_errno(dtp, errno));
479
480	for (i = 0; i < prp->pr_nargc; i++) {
481		if (fprintf(infop->dthi_out, "arg%d", i) < 0)
482			return (dt_set_errno(dtp, errno));
483
484		if (i + 1 != prp->pr_nargc &&
485		    fprintf(infop->dthi_out, ", ") < 0)
486			return (dt_set_errno(dtp, errno));
487	}
488
489	if (!infop->dthi_empty) {
490		if (fprintf(infop->dthi_out, ") \\\n\t") < 0)
491			return (dt_set_errno(dtp, errno));
492
493		if (fprintf(infop->dthi_out, "__dtrace_%s___%s(",
494		    infop->dthi_pfname, fname) < 0)
495			return (dt_set_errno(dtp, errno));
496
497		for (i = 0; i < prp->pr_nargc; i++) {
498			if (fprintf(infop->dthi_out, "arg%d", i) < 0)
499				return (dt_set_errno(dtp, errno));
500
501			if (i + 1 != prp->pr_nargc &&
502			    fprintf(infop->dthi_out, ", ") < 0)
503				return (dt_set_errno(dtp, errno));
504		}
505	}
506
507	if (fprintf(infop->dthi_out, ")\n") < 0)
508		return (dt_set_errno(dtp, errno));
509
510	if (!infop->dthi_empty) {
511		if (fprintf(infop->dthi_out,
512		    "#ifndef\t__sparc\n"
513		    "#define\t%s_%s_ENABLED() \\\n"
514		    "\t__dtraceenabled_%s___%s()\n"
515		    "#else\n"
516		    "#define\t%s_%s_ENABLED() \\\n"
517		    "\t__dtraceenabled_%s___%s(0)\n"
518		    "#endif\n",
519		    infop->dthi_pmname, mname,
520		    infop->dthi_pfname, fname,
521		    infop->dthi_pmname, mname,
522		    infop->dthi_pfname, fname) < 0)
523			return (dt_set_errno(dtp, errno));
524
525	} else {
526		if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n",
527		    infop->dthi_pmname, mname) < 0)
528			return (dt_set_errno(dtp, errno));
529	}
530
531	return (0);
532}
533
534static int
535dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out)
536{
537	dt_header_info_t info;
538	const char *p;
539	int i;
540
541	if (pvp->pv_flags & DT_PROVIDER_IMPL)
542		return (0);
543
544	/*
545	 * Count the instances of the '-' character since we'll need to double
546	 * those up.
547	 */
548	p = pvp->pv_desc.dtvd_name;
549	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
550		p++;
551
552	info.dthi_dtp = dtp;
553	info.dthi_out = out;
554	info.dthi_empty = 0;
555
556	info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1);
557	dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name);
558
559	info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i);
560	dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name);
561
562#ifdef __FreeBSD__
563	if (fprintf(out, "#include <sys/sdt.h>\n\n") < 0)
564		return (dt_set_errno(dtp, errno));
565#endif
566	if (fprintf(out, "#if _DTRACE_VERSION\n\n") < 0)
567		return (dt_set_errno(dtp, errno));
568
569	if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
570		return (-1); /* dt_errno is set for us */
571	if (fprintf(out, "\n\n") < 0)
572		return (dt_set_errno(dtp, errno));
573	if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0)
574		return (-1); /* dt_errno is set for us */
575
576	if (fprintf(out, "\n#else\n\n") < 0)
577		return (dt_set_errno(dtp, errno));
578
579	info.dthi_empty = 1;
580
581	if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
582		return (-1); /* dt_errno is set for us */
583
584	if (fprintf(out, "\n#endif\n\n") < 0)
585		return (dt_set_errno(dtp, errno));
586
587	return (0);
588}
589
590int
591dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname)
592{
593	dt_provider_t *pvp;
594	char *mfname, *p;
595
596	if (fname != NULL) {
597		if ((p = strrchr(fname, '/')) != NULL)
598			fname = p + 1;
599
600		mfname = alloca(strlen(fname) + 1);
601		dt_header_fmt_macro(mfname, fname);
602		if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n",
603		    mfname, mfname) < 0)
604			return (dt_set_errno(dtp, errno));
605	}
606
607	if (fprintf(out, "#include <unistd.h>\n\n") < 0)
608		return (-1);
609
610	if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0)
611		return (-1);
612
613	for (pvp = dt_list_next(&dtp->dt_provlist);
614	    pvp != NULL; pvp = dt_list_next(pvp)) {
615		if (dt_header_provider(dtp, pvp, out) != 0)
616			return (-1); /* dt_errno is set for us */
617	}
618
619	if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0)
620		return (dt_set_errno(dtp, errno));
621
622	if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0)
623		return (dt_set_errno(dtp, errno));
624
625	return (0);
626}
627