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#if defined(sun)
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
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
62void
63dt_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*/
83void
84dtrace_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
150int
151dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
152    dtrace_proginfo_t *pip)
153{
154	dtrace_enable_io_t args;
155	void *dof;
156	int n, err;
157
158	dtrace_program_info(dtp, pgp, pip);
159
160	if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
161		return (-1);
162
163	args.dof = dof;
164	args.n_matched = 0;
165	n = dt_ioctl(dtp, DTRACEIOC_ENABLE, &args);
166	dtrace_dof_destroy(dtp, dof);
167
168	if (n == -1) {
169		switch (errno) {
170		case EINVAL:
171			err = EDT_DIFINVAL;
172			break;
173		case EFAULT:
174			err = EDT_DIFFAULT;
175			break;
176		case E2BIG:
177			err = EDT_DIFSIZE;
178			break;
179		case EBUSY:
180			err = EDT_ENABLING_ERR;
181			break;
182		default:
183			err = errno;
184		}
185
186		return (dt_set_errno(dtp, err));
187	}
188
189	if (pip != NULL)
190		pip->dpi_matches += args.n_matched;
191
192	return (0);
193}
194
195static void
196dt_ecbdesc_hold(dtrace_ecbdesc_t *edp)
197{
198	edp->dted_refcnt++;
199}
200
201void
202dt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
203{
204	if (--edp->dted_refcnt > 0)
205		return;
206
207	dt_difo_free(dtp, edp->dted_pred.dtpdd_difo);
208	assert(edp->dted_action == NULL);
209	dt_free(dtp, edp);
210}
211
212dtrace_ecbdesc_t *
213dt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
214{
215	dtrace_ecbdesc_t *edp;
216
217	if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) {
218		(void) dt_set_errno(dtp, EDT_NOMEM);
219		return (NULL);
220	}
221
222	edp->dted_probe = *pdp;
223	dt_ecbdesc_hold(edp);
224	return (edp);
225}
226
227dtrace_stmtdesc_t *
228dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
229{
230	dtrace_stmtdesc_t *sdp;
231
232	if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL)
233		return (NULL);
234
235	dt_ecbdesc_hold(edp);
236	sdp->dtsd_ecbdesc = edp;
237	sdp->dtsd_descattr = _dtrace_defattr;
238	sdp->dtsd_stmtattr = _dtrace_defattr;
239
240	return (sdp);
241}
242
243dtrace_actdesc_t *
244dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
245{
246	dtrace_actdesc_t *new;
247	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
248
249	if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL)
250		return (NULL);
251
252	if (sdp->dtsd_action_last != NULL) {
253		assert(sdp->dtsd_action != NULL);
254		assert(sdp->dtsd_action_last->dtad_next == NULL);
255		sdp->dtsd_action_last->dtad_next = new;
256	} else {
257		dtrace_actdesc_t *ap = edp->dted_action;
258
259		assert(sdp->dtsd_action == NULL);
260		sdp->dtsd_action = new;
261
262		while (ap != NULL && ap->dtad_next != NULL)
263			ap = ap->dtad_next;
264
265		if (ap == NULL)
266			edp->dted_action = new;
267		else
268			ap->dtad_next = new;
269	}
270
271	sdp->dtsd_action_last = new;
272	bzero(new, sizeof (dtrace_actdesc_t));
273	new->dtad_uarg = (uintptr_t)sdp;
274
275	return (new);
276}
277
278int
279dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
280{
281	dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t));
282
283	if (stp == NULL)
284		return (-1); /* errno is set for us */
285
286	dt_list_append(&pgp->dp_stmts, stp);
287	stp->ds_desc = sdp;
288
289	return (0);
290}
291
292int
293dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
294    dtrace_stmt_f *func, void *data)
295{
296	dt_stmt_t *stp, *next;
297	int status = 0;
298
299	for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
300		next = dt_list_next(stp);
301		if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
302			break;
303	}
304
305	return (status);
306}
307
308void
309dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
310{
311	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
312
313	/*
314	 * We need to remove any actions that we have on this ECB, and
315	 * remove our hold on the ECB itself.
316	 */
317	if (sdp->dtsd_action != NULL) {
318		dtrace_actdesc_t *last = sdp->dtsd_action_last;
319		dtrace_actdesc_t *ap, *next;
320
321		assert(last != NULL);
322
323		for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
324			if (ap == sdp->dtsd_action)
325				break;
326
327			if (ap->dtad_next == sdp->dtsd_action)
328				break;
329		}
330
331		assert(ap != NULL);
332
333		if (ap == edp->dted_action)
334			edp->dted_action = last->dtad_next;
335		else
336			ap->dtad_next = last->dtad_next;
337
338		/*
339		 * We have now removed our action list from its ECB; we can
340		 * safely destroy the list.
341		 */
342		last->dtad_next = NULL;
343
344		for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
345			assert(ap->dtad_uarg == (uintptr_t)sdp);
346			dt_difo_free(dtp, ap->dtad_difo);
347			next = ap->dtad_next;
348			dt_free(dtp, ap);
349		}
350	}
351
352	if (sdp->dtsd_fmtdata != NULL)
353		dt_printf_destroy(sdp->dtsd_fmtdata);
354
355	dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
356	dt_free(dtp, sdp);
357}
358
359typedef struct dt_header_info {
360	dtrace_hdl_t *dthi_dtp;	/* consumer handle */
361	FILE *dthi_out;		/* output file */
362	char *dthi_pmname;	/* provider macro name */
363	char *dthi_pfname;	/* provider function name */
364	int dthi_empty;		/* should we generate empty macros */
365} dt_header_info_t;
366
367static void
368dt_header_fmt_macro(char *buf, const char *str)
369{
370	for (;;) {
371		if (islower(*str)) {
372			*buf++ = *str++ + 'A' - 'a';
373		} else if (*str == '-') {
374			*buf++ = '_';
375			str++;
376		} else if (*str == '.') {
377			*buf++ = '_';
378			str++;
379		} else if ((*buf++ = *str++) == '\0') {
380			break;
381		}
382	}
383}
384
385static void
386dt_header_fmt_func(char *buf, const char *str)
387{
388	for (;;) {
389		if (*str == '-') {
390			*buf++ = '_';
391			*buf++ = '_';
392			str++;
393		} else if ((*buf++ = *str++) == '\0') {
394			break;
395		}
396	}
397}
398
399/*ARGSUSED*/
400static int
401dt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
402{
403	dt_header_info_t *infop = data;
404	dtrace_hdl_t *dtp = infop->dthi_dtp;
405	dt_probe_t *prp = idp->di_data;
406	dt_node_t *dnp;
407	char buf[DT_TYPE_NAMELEN];
408	char *fname;
409	const char *p;
410	int i;
411
412	p = prp->pr_name;
413	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
414		p++;
415
416	fname = alloca(strlen(prp->pr_name) + 1 + i);
417	dt_header_fmt_func(fname, prp->pr_name);
418
419	if (fprintf(infop->dthi_out, "extern void __dtrace_%s___%s(",
420	    infop->dthi_pfname, fname) < 0)
421		return (dt_set_errno(dtp, errno));
422
423	for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
424		if (fprintf(infop->dthi_out, "%s",
425		    ctf_type_name(dnp->dn_ctfp, dnp->dn_type,
426		    buf, sizeof (buf))) < 0)
427			return (dt_set_errno(dtp, errno));
428
429		if (i + 1 != prp->pr_nargc &&
430		    fprintf(infop->dthi_out, ", ") < 0)
431			return (dt_set_errno(dtp, errno));
432	}
433
434	if (i == 0 && fprintf(infop->dthi_out, "void") < 0)
435		return (dt_set_errno(dtp, errno));
436
437	if (fprintf(infop->dthi_out, ");\n") < 0)
438		return (dt_set_errno(dtp, errno));
439
440	if (fprintf(infop->dthi_out,
441	    "#ifndef\t__sparc\n"
442	    "extern int __dtraceenabled_%s___%s(void);\n"
443	    "#else\n"
444	    "extern int __dtraceenabled_%s___%s(long);\n"
445	    "#endif\n",
446	    infop->dthi_pfname, fname, infop->dthi_pfname, fname) < 0)
447		return (dt_set_errno(dtp, errno));
448
449	return (0);
450}
451
452/*ARGSUSED*/
453static int
454dt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
455{
456	dt_header_info_t *infop = data;
457	dtrace_hdl_t *dtp = infop->dthi_dtp;
458	dt_probe_t *prp = idp->di_data;
459	char *mname, *fname;
460	const char *p;
461	int i;
462
463	p = prp->pr_name;
464	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
465		p++;
466
467	mname = alloca(strlen(prp->pr_name) + 1);
468	dt_header_fmt_macro(mname, prp->pr_name);
469
470	fname = alloca(strlen(prp->pr_name) + 1 + i);
471	dt_header_fmt_func(fname, prp->pr_name);
472
473	if (fprintf(infop->dthi_out, "#define\t%s_%s(",
474	    infop->dthi_pmname, mname) < 0)
475		return (dt_set_errno(dtp, errno));
476
477	for (i = 0; i < prp->pr_nargc; i++) {
478		if (fprintf(infop->dthi_out, "arg%d", i) < 0)
479			return (dt_set_errno(dtp, errno));
480
481		if (i + 1 != prp->pr_nargc &&
482		    fprintf(infop->dthi_out, ", ") < 0)
483			return (dt_set_errno(dtp, errno));
484	}
485
486	if (!infop->dthi_empty) {
487		if (fprintf(infop->dthi_out, ") \\\n\t") < 0)
488			return (dt_set_errno(dtp, errno));
489
490		if (fprintf(infop->dthi_out, "__dtrace_%s___%s(",
491		    infop->dthi_pfname, fname) < 0)
492			return (dt_set_errno(dtp, errno));
493
494		for (i = 0; i < prp->pr_nargc; i++) {
495			if (fprintf(infop->dthi_out, "arg%d", i) < 0)
496				return (dt_set_errno(dtp, errno));
497
498			if (i + 1 != prp->pr_nargc &&
499			    fprintf(infop->dthi_out, ", ") < 0)
500				return (dt_set_errno(dtp, errno));
501		}
502	}
503
504	if (fprintf(infop->dthi_out, ")\n") < 0)
505		return (dt_set_errno(dtp, errno));
506
507	if (!infop->dthi_empty) {
508		if (fprintf(infop->dthi_out,
509		    "#ifndef\t__sparc\n"
510		    "#define\t%s_%s_ENABLED() \\\n"
511		    "\t__dtraceenabled_%s___%s()\n"
512		    "#else\n"
513		    "#define\t%s_%s_ENABLED() \\\n"
514		    "\t__dtraceenabled_%s___%s(0)\n"
515		    "#endif\n",
516		    infop->dthi_pmname, mname,
517		    infop->dthi_pfname, fname,
518		    infop->dthi_pmname, mname,
519		    infop->dthi_pfname, fname) < 0)
520			return (dt_set_errno(dtp, errno));
521
522	} else {
523		if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n",
524		    infop->dthi_pmname, mname) < 0)
525			return (dt_set_errno(dtp, errno));
526	}
527
528	return (0);
529}
530
531static int
532dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out)
533{
534	dt_header_info_t info;
535	const char *p;
536	int i;
537
538	if (pvp->pv_flags & DT_PROVIDER_IMPL)
539		return (0);
540
541	/*
542	 * Count the instances of the '-' character since we'll need to double
543	 * those up.
544	 */
545	p = pvp->pv_desc.dtvd_name;
546	for (i = 0; (p = strchr(p, '-')) != NULL; i++)
547		p++;
548
549	info.dthi_dtp = dtp;
550	info.dthi_out = out;
551	info.dthi_empty = 0;
552
553	info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1);
554	dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name);
555
556	info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i);
557	dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name);
558
559	if (fprintf(out, "#if _DTRACE_VERSION\n\n") < 0)
560		return (dt_set_errno(dtp, errno));
561
562	if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
563		return (-1); /* dt_errno is set for us */
564	if (fprintf(out, "\n\n") < 0)
565		return (dt_set_errno(dtp, errno));
566	if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0)
567		return (-1); /* dt_errno is set for us */
568
569	if (fprintf(out, "\n#else\n\n") < 0)
570		return (dt_set_errno(dtp, errno));
571
572	info.dthi_empty = 1;
573
574	if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
575		return (-1); /* dt_errno is set for us */
576
577	if (fprintf(out, "\n#endif\n\n") < 0)
578		return (dt_set_errno(dtp, errno));
579
580	return (0);
581}
582
583int
584dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname)
585{
586	dt_provider_t *pvp;
587	char *mfname, *p;
588
589	if (fname != NULL) {
590		if ((p = strrchr(fname, '/')) != NULL)
591			fname = p + 1;
592
593		mfname = alloca(strlen(fname) + 1);
594		dt_header_fmt_macro(mfname, fname);
595		if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n",
596		    mfname, mfname) < 0)
597			return (dt_set_errno(dtp, errno));
598	}
599
600	if (fprintf(out, "#include <unistd.h>\n\n") < 0)
601		return (-1);
602
603	if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0)
604		return (-1);
605
606	for (pvp = dt_list_next(&dtp->dt_provlist);
607	    pvp != NULL; pvp = dt_list_next(pvp)) {
608		if (dt_header_provider(dtp, pvp, out) != 0)
609			return (-1); /* dt_errno is set for us */
610	}
611
612	if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0)
613		return (dt_set_errno(dtp, errno));
614
615	if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0)
616		return (dt_set_errno(dtp, errno));
617
618	return (0);
619}
620