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