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