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