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