xref: /illumos-gate/usr/src/lib/libdtrace/common/dt_program.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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  * Copyright 2005 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 
35 #include <dt_impl.h>
36 #include <dt_printf.h>
37 
38 dtrace_prog_t *
39 dtrace_program_create(dtrace_hdl_t *dtp)
40 {
41 	dtrace_prog_t *pgp = calloc(1, sizeof (dtrace_prog_t));
42 
43 	if (pgp != NULL)
44 		dt_list_append(&dtp->dt_programs, pgp);
45 	else
46 		(void) dt_set_errno(dtp, EDT_NOMEM);
47 
48 	return (pgp);
49 }
50 
51 void
52 dtrace_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp)
53 {
54 	dt_stmt_t *stp, *next;
55 
56 	for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
57 		next = dt_list_next(stp);
58 		dtrace_stmt_destroy(stp->ds_desc);
59 		free(stp);
60 	}
61 
62 	dt_list_delete(&dtp->dt_programs, pgp);
63 	free(pgp);
64 }
65 
66 /*ARGSUSED*/
67 void
68 dtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
69     dtrace_proginfo_t *pip)
70 {
71 	dt_stmt_t *stp;
72 	dtrace_actdesc_t *ap;
73 	dtrace_ecbdesc_t *last = NULL;
74 
75 	if (pip == NULL)
76 		return;
77 
78 	bzero(pip, sizeof (dtrace_proginfo_t));
79 
80 	if (dt_list_next(&pgp->dp_stmts) != NULL) {
81 		pip->dpi_descattr = _dtrace_maxattr;
82 		pip->dpi_stmtattr = _dtrace_maxattr;
83 	} else {
84 		pip->dpi_descattr = _dtrace_defattr;
85 		pip->dpi_stmtattr = _dtrace_defattr;
86 	}
87 
88 	for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) {
89 		dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc;
90 
91 		if (edp == last)
92 			continue;
93 		last = edp;
94 
95 		pip->dpi_descattr =
96 		    dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr);
97 
98 		pip->dpi_stmtattr =
99 		    dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr);
100 
101 		/*
102 		 * If there aren't any actions, account for the fact that
103 		 * recording the epid will generate a record.
104 		 */
105 		if (edp->dted_action == NULL)
106 			pip->dpi_recgens++;
107 
108 		for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
109 			if (ap->dtad_kind == DTRACEACT_SPECULATE) {
110 				pip->dpi_speculations++;
111 				continue;
112 			}
113 
114 			if (DTRACEACT_ISAGG(ap->dtad_kind)) {
115 				pip->dpi_recgens -= ap->dtad_arg;
116 				pip->dpi_aggregates++;
117 				continue;
118 			}
119 
120 			if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind))
121 				continue;
122 
123 			if (ap->dtad_kind == DTRACEACT_DIFEXPR &&
124 			    ap->dtad_difo->dtdo_rtype.dtdt_kind ==
125 			    DIF_TYPE_CTF &&
126 			    ap->dtad_difo->dtdo_rtype.dtdt_size == 0)
127 				continue;
128 
129 			pip->dpi_recgens++;
130 		}
131 	}
132 }
133 
134 int
135 dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
136     dtrace_proginfo_t *pip)
137 {
138 	void *dof;
139 	int n, err;
140 
141 	dtrace_program_info(dtp, pgp, pip);
142 
143 	if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
144 		return (-1);
145 
146 	n = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof);
147 	dtrace_dof_destroy(dtp, dof);
148 
149 	if (n == -1) {
150 		switch (errno) {
151 		case EINVAL:
152 			err = EDT_DIFINVAL;
153 			break;
154 		case EFAULT:
155 			err = EDT_DIFFAULT;
156 			break;
157 		case E2BIG:
158 			err = EDT_DIFSIZE;
159 			break;
160 		default:
161 			err = errno;
162 		}
163 
164 		return (dt_set_errno(dtp, err));
165 	}
166 
167 	if (pip != NULL)
168 		pip->dpi_matches += n;
169 
170 	return (0);
171 }
172 
173 void
174 dtrace_ecbdesc_hold(dtrace_ecbdesc_t *edp)
175 {
176 	edp->dted_refcnt++;
177 }
178 
179 void
180 dtrace_ecbdesc_release(dtrace_ecbdesc_t *edp)
181 {
182 	dtrace_difo_t *dp;
183 
184 	if (--edp->dted_refcnt > 0)
185 		return;
186 
187 	if ((dp = edp->dted_pred.dtpdd_difo) != NULL)
188 		dtrace_difo_release(dp);
189 
190 	assert(edp->dted_action == NULL);
191 	free(edp);
192 }
193 
194 dtrace_ecbdesc_t *
195 dtrace_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
196 {
197 	dtrace_ecbdesc_t *edp;
198 
199 	if ((edp = malloc(sizeof (dtrace_ecbdesc_t))) == NULL) {
200 		(void) dt_set_errno(dtp, EDT_NOMEM);
201 		return (NULL);
202 	}
203 
204 	bzero(edp, sizeof (dtrace_ecbdesc_t));
205 	edp->dted_probe = *pdp;
206 	dtrace_ecbdesc_hold(edp);
207 
208 	return (edp);
209 }
210 
211 dtrace_stmtdesc_t *
212 dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
213 {
214 	dtrace_stmtdesc_t *sdp;
215 
216 	if ((sdp = malloc(sizeof (dtrace_stmtdesc_t))) == NULL) {
217 		(void) dt_set_errno(dtp, EDT_NOMEM);
218 		return (NULL);
219 	}
220 
221 	bzero(sdp, sizeof (dtrace_stmtdesc_t));
222 	dtrace_ecbdesc_hold(edp);
223 	sdp->dtsd_ecbdesc = edp;
224 	sdp->dtsd_descattr = _dtrace_defattr;
225 	sdp->dtsd_stmtattr = _dtrace_defattr;
226 
227 	return (sdp);
228 }
229 
230 dtrace_actdesc_t *
231 dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
232 {
233 	dtrace_actdesc_t *new;
234 	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
235 
236 	if ((new = malloc(sizeof (dtrace_actdesc_t))) == NULL) {
237 		(void) dt_set_errno(dtp, EDT_NOMEM);
238 		return (NULL);
239 	}
240 
241 	if (sdp->dtsd_action_last != NULL) {
242 		assert(sdp->dtsd_action != NULL);
243 		assert(sdp->dtsd_action_last->dtad_next == NULL);
244 		sdp->dtsd_action_last->dtad_next = new;
245 	} else {
246 		dtrace_actdesc_t *ap = edp->dted_action;
247 
248 		assert(sdp->dtsd_action == NULL);
249 		sdp->dtsd_action = new;
250 
251 		while (ap != NULL && ap->dtad_next != NULL)
252 			ap = ap->dtad_next;
253 
254 		if (ap == NULL)
255 			edp->dted_action = new;
256 		else
257 			ap->dtad_next = new;
258 	}
259 
260 	sdp->dtsd_action_last = new;
261 	bzero(new, sizeof (dtrace_actdesc_t));
262 	new->dtad_uarg = (uintptr_t)sdp;
263 
264 	return (new);
265 }
266 
267 int
268 dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
269 {
270 	dt_stmt_t *stp = malloc(sizeof (dt_stmt_t));
271 
272 	if (stp == NULL)
273 		return (dt_set_errno(dtp, EDT_NOMEM));
274 
275 	dt_list_append(&pgp->dp_stmts, stp);
276 	stp->ds_desc = sdp;
277 
278 	return (0);
279 }
280 
281 int
282 dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
283     dtrace_stmt_f *func, void *data)
284 {
285 	dt_stmt_t *stp, *next;
286 	int status = 0;
287 
288 	for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
289 		next = dt_list_next(stp);
290 		if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
291 			break;
292 	}
293 
294 	return (status);
295 }
296 
297 void
298 dtrace_stmt_destroy(dtrace_stmtdesc_t *sdp)
299 {
300 	dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
301 
302 	/*
303 	 * We need to remove any actions that we have on this ECB, and
304 	 * remove our hold on the ECB itself.
305 	 */
306 	if (sdp->dtsd_action != NULL) {
307 		dtrace_actdesc_t *last = sdp->dtsd_action_last;
308 		dtrace_actdesc_t *ap, *next;
309 
310 		assert(last != NULL);
311 
312 		for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
313 			if (ap == sdp->dtsd_action)
314 				break;
315 
316 			if (ap->dtad_next == sdp->dtsd_action)
317 				break;
318 		}
319 
320 		assert(ap != NULL);
321 
322 		if (ap == edp->dted_action) {
323 			edp->dted_action = last->dtad_next;
324 		} else {
325 			ap->dtad_next = last->dtad_next;
326 		}
327 
328 		/*
329 		 * We have now removed our action list from its ECB; we can
330 		 * safely destroy the list.
331 		 */
332 		last->dtad_next = NULL;
333 
334 		for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
335 			dtrace_difo_t *dp;
336 
337 			assert(ap->dtad_uarg == (uintptr_t)sdp);
338 
339 			if ((dp = ap->dtad_difo) != NULL)
340 				dtrace_difo_release(dp);
341 
342 			next = ap->dtad_next;
343 			free(ap);
344 		}
345 	}
346 
347 	if (sdp->dtsd_fmtdata != NULL)
348 		dt_printf_destroy(sdp->dtsd_fmtdata);
349 
350 	dtrace_ecbdesc_release(sdp->dtsd_ecbdesc);
351 	free(sdp);
352 }
353