1 /*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14 */
15
16 /*
17 * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
18 */
19
20 /*
21 * These syntactic sugar features are implemented by transforming the D
22 * parse tree such that it only uses the subset of D that is supported
23 * by the rest of the compiler / the kernel. A clause containing these
24 * language features is referred to as a "super-clause", and its
25 * transformation typically entails creating several "sub-clauses" to
26 * implement it. For diagnosability, the sub-clauses will be printed if
27 * the "-xtree=8" flag is specified.
28 *
29 * The features are:
30 *
31 * "if/else" statements. Each basic block (e.g. the body of the "if"
32 * and "else" statements, and the statements before and after) is turned
33 * into its own sub-clause, with a predicate that causes it to be
34 * executed only if the code flows to this point. Nested if/else
35 * statements are supported.
36 */
37
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <sys/sysmacros.h>
41
42 #include <assert.h>
43 #include <strings.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <dt_module.h>
48 #include <dt_program.h>
49 #include <dt_provider.h>
50 #include <dt_printf.h>
51 #include <dt_pid.h>
52 #include <dt_grammar.h>
53 #include <dt_ident.h>
54 #include <dt_string.h>
55 #include <dt_impl.h>
56
57 typedef struct xd_parse {
58 dtrace_hdl_t *xp_dtp;
59 dt_node_t *xp_pdescs; /* probe descriptions */
60 int xp_num_conditions;
61 int xp_num_ifs;
62 dt_node_t *xp_clause_list;
63 } xd_parse_t;
64
65 static void xd_visit_stmts(xd_parse_t *, dt_node_t *, int);
66
67 /*
68 * Return a node for "self->%error".
69 *
70 * Note that the "%" is part of the variable name, and is included so that
71 * this variable name can not collide with any user-specified variable.
72 *
73 * This error variable is used to keep track of if there has been an error
74 * in any of the sub-clauses, and is used to prevent execution of subsequent
75 * sub-clauses following an error.
76 */
77 static dt_node_t *
78 xd_new_error_var(void)
79 {
80 return (dt_node_op2(DT_TOK_PTR, dt_node_ident(strdup("self")),
81 dt_node_ident(strdup("%error"))));
82 }
83
84 /*
85 * Append this clause to the clause list.
86 */
87 static void
88 xd_append_clause(xd_parse_t *dp, dt_node_t *clause)
89 {
90 dp->xp_clause_list = dt_node_link(dp->xp_clause_list, clause);
91 }
92
93 /*
94 * Prepend this clause to the clause list.
95 */
96 static void
97 xd_prepend_clause(xd_parse_t *dp, dt_node_t *clause)
98 {
99 dp->xp_clause_list = dt_node_link(clause, dp->xp_clause_list);
100 }
101
102 /*
103 * Return a node for "this->%condition_<condid>", or NULL if condid==0.
104 *
105 * Note that the "%" is part of the variable name, and is included so that
106 * this variable name can not collide with any user-specified variable.
107 */
108 static dt_node_t *
109 xd_new_condition_var(int condid)
110 {
111 char *str;
112
113 if (condid == 0)
114 return (NULL);
115 assert(condid > 0);
116
117 (void) asprintf(&str, "%%condition_%d", ABS(condid));
118 return (dt_node_op2(DT_TOK_PTR, dt_node_ident(strdup("this")),
119 dt_node_ident(str)));
120 }
121
122 /*
123 * Return new clause to evaluate predicate and set newcond. condid is
124 * the condition that we are already under, or 0 if none.
125 * The new clause will be of the form:
126 *
127 * dp_pdescs
128 * /!self->%error/
129 * {
130 * this->%condition_<newcond> =
131 * (this->%condition_<condid> && pred);
132 * }
133 *
134 * Note: if condid==0, we will instead do "... = (1 && pred)", to effectively
135 * convert the pred to a boolean.
136 *
137 * Note: Unless an error has been encountered, we always set the condition
138 * variable (either to 0 or 1). This lets us avoid resetting the condition
139 * variables back to 0 when the super-clause completes.
140 */
141 static dt_node_t *
142 xd_new_condition_impl(xd_parse_t *dp, dt_node_t *pred, int condid, int newcond)
143 {
144 dt_node_t *value, *body, *newpred;
145
146 /* predicate is !self->%error */
147 newpred = dt_node_op1(DT_TOK_LNEG, xd_new_error_var());
148
149 if (condid == 0) {
150 /*
151 * value is (1 && pred)
152 *
153 * Note, D doesn't allow a probe-local "this" variable to
154 * be reused as a different type, even from a different probe.
155 * Therefore, value can't simply be <pred>, because then
156 * its type could be different when we reuse this condid
157 * in a different meta-clause.
158 */
159 value = dt_node_op2(DT_TOK_LAND, dt_node_int(1), pred);
160 } else {
161 /* value is (this->%condition_<condid> && pred) */
162 value = dt_node_op2(DT_TOK_LAND,
163 xd_new_condition_var(condid), pred);
164 }
165
166 /* body is "this->%condition_<retval> = <value>;" */
167 body = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
168 xd_new_condition_var(newcond), value));
169
170 return (dt_node_clause(dp->xp_pdescs, newpred, body));
171 }
172
173 /*
174 * Generate a new clause to evaluate predicate and set a new condition variable,
175 * whose ID will be returned. The new clause will be appended to
176 * dp_first_new_clause.
177 */
178 static int
179 xd_new_condition(xd_parse_t *dp, dt_node_t *pred, int condid)
180 {
181 dp->xp_num_conditions++;
182 xd_append_clause(dp, xd_new_condition_impl(dp,
183 pred, condid, dp->xp_num_conditions));
184 return (dp->xp_num_conditions);
185 }
186
187 /*
188 * Visit the specified node and all of its descendants. Replace any
189 * entry_* variables (e.g. entry_walltimestamp,entry_args[1], entry_elapsed_us)
190 * and callers[""] variables (e.g. callers["spa_sync"]) with the corresponding
191 * straight-D nodes.
192 */
193 static void
194 xd_visit_all(xd_parse_t *dp, dt_node_t *dnp)
195 {
196 dt_node_t *arg;
197
198 switch (dnp->dn_kind) {
199 case DT_NODE_FREE:
200 case DT_NODE_INT:
201 case DT_NODE_STRING:
202 case DT_NODE_SYM:
203 case DT_NODE_TYPE:
204 case DT_NODE_PROBE:
205 case DT_NODE_PDESC:
206 case DT_NODE_IDENT:
207 break;
208
209 case DT_NODE_FUNC:
210 for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list)
211 xd_visit_all(dp, arg);
212 break;
213
214 case DT_NODE_OP1:
215 xd_visit_all(dp, dnp->dn_child);
216 break;
217
218 case DT_NODE_OP2:
219 xd_visit_all(dp, dnp->dn_left);
220 xd_visit_all(dp, dnp->dn_right);
221 if (dnp->dn_op == DT_TOK_LBRAC) {
222 dt_node_t *ln = dnp->dn_right;
223 while (ln->dn_list != NULL) {
224 xd_visit_all(dp, ln->dn_list);
225 ln = ln->dn_list;
226 }
227 }
228 break;
229
230 case DT_NODE_OP3:
231 xd_visit_all(dp, dnp->dn_expr);
232 xd_visit_all(dp, dnp->dn_left);
233 xd_visit_all(dp, dnp->dn_right);
234 break;
235
236 case DT_NODE_DEXPR:
237 case DT_NODE_DFUNC:
238 xd_visit_all(dp, dnp->dn_expr);
239 break;
240
241 case DT_NODE_AGG:
242
243 for (arg = dnp->dn_aggtup; arg != NULL; arg = arg->dn_list)
244 xd_visit_all(dp, arg);
245
246 if (dnp->dn_aggfun)
247 xd_visit_all(dp, dnp->dn_aggfun);
248 break;
249
250 case DT_NODE_CLAUSE:
251 for (arg = dnp->dn_pdescs; arg != NULL; arg = arg->dn_list)
252 xd_visit_all(dp, arg);
253
254 if (dnp->dn_pred != NULL)
255 xd_visit_all(dp, dnp->dn_pred);
256
257 for (arg = dnp->dn_acts; arg != NULL; arg = arg->dn_list)
258 xd_visit_all(dp, arg);
259 break;
260
261 case DT_NODE_INLINE: {
262 const dt_idnode_t *inp = dnp->dn_ident->di_iarg;
263
264 xd_visit_all(dp, inp->din_root);
265 break;
266 }
267 case DT_NODE_MEMBER:
268 if (dnp->dn_membexpr)
269 xd_visit_all(dp, dnp->dn_membexpr);
270 break;
271
272 case DT_NODE_XLATOR:
273 for (arg = dnp->dn_members; arg != NULL; arg = arg->dn_list)
274 xd_visit_all(dp, arg);
275 break;
276
277 case DT_NODE_PROVIDER:
278 for (arg = dnp->dn_probes; arg != NULL; arg = arg->dn_list)
279 xd_visit_all(dp, arg);
280 break;
281
282 case DT_NODE_PROG:
283 for (arg = dnp->dn_list; arg != NULL; arg = arg->dn_list)
284 xd_visit_all(dp, arg);
285 break;
286
287 case DT_NODE_IF:
288 dp->xp_num_ifs++;
289 xd_visit_all(dp, dnp->dn_conditional);
290
291 for (arg = dnp->dn_body; arg != NULL; arg = arg->dn_list)
292 xd_visit_all(dp, arg);
293 for (arg = dnp->dn_alternate_body; arg != NULL;
294 arg = arg->dn_list)
295 xd_visit_all(dp, arg);
296
297 break;
298
299 default:
300 (void) dnerror(dnp, D_UNKNOWN, "bad node %p, kind %d\n",
301 (void *)dnp, dnp->dn_kind);
302 }
303 }
304
305 /*
306 * Return a new clause which resets the error variable to zero:
307 *
308 * dp_pdescs{ self->%error = 0; }
309 *
310 * This clause will be executed at the beginning of each meta-clause, to
311 * ensure the error variable is unset (in case the previous meta-clause
312 * failed).
313 */
314 static dt_node_t *
315 xd_new_clearerror_clause(xd_parse_t *dp)
316 {
317 dt_node_t *stmt = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
318 xd_new_error_var(), dt_node_int(0)));
319 return (dt_node_clause(dp->xp_pdescs, NULL, stmt));
320 }
321
322 /*
323 * Evaluate the conditional, and recursively visit the body of the "if"
324 * statement (and the "else", if present).
325 */
326 static void
327 xd_do_if(xd_parse_t *dp, dt_node_t *if_stmt, int precondition)
328 {
329 int newid;
330
331 assert(if_stmt->dn_kind == DT_NODE_IF);
332
333 /* condition */
334 newid = xd_new_condition(dp, if_stmt->dn_conditional, precondition);
335
336 /* body of if */
337 xd_visit_stmts(dp, if_stmt->dn_body, newid);
338
339 /*
340 * Visit the body of the "else" statement, if present. Note that we
341 * generate a new condition which is the inverse of the previous
342 * condition.
343 */
344 if (if_stmt->dn_alternate_body != NULL) {
345 dt_node_t *pred =
346 dt_node_op1(DT_TOK_LNEG, xd_new_condition_var(newid));
347 xd_visit_stmts(dp, if_stmt->dn_alternate_body,
348 xd_new_condition(dp, pred, precondition));
349 }
350 }
351
352 /*
353 * Generate a new clause to evaluate the statements based on the condition.
354 * The new clause will be appended to dp_first_new_clause.
355 *
356 * dp_pdescs
357 * /!self->%error && this->%condition_<condid>/
358 * {
359 * stmts
360 * }
361 */
362 static void
363 xd_new_basic_block(xd_parse_t *dp, int condid, dt_node_t *stmts)
364 {
365 dt_node_t *pred = NULL;
366
367 if (condid == 0) {
368 /*
369 * Don't bother with !error on the first clause, because if
370 * there is only one clause, we don't add the prelude to
371 * zero out %error.
372 */
373 if (dp->xp_num_conditions != 0)
374 pred = dt_node_op1(DT_TOK_LNEG, xd_new_error_var());
375 } else {
376 pred = dt_node_op2(DT_TOK_LAND,
377 dt_node_op1(DT_TOK_LNEG, xd_new_error_var()),
378 xd_new_condition_var(condid));
379 }
380 xd_append_clause(dp,
381 dt_node_clause(dp->xp_pdescs, pred, stmts));
382 }
383
384 /*
385 * Visit all the statements in this list, and break them into basic blocks,
386 * generating new clauses for "if" and "else" statements.
387 */
388 static void
389 xd_visit_stmts(xd_parse_t *dp, dt_node_t *stmts, int precondition)
390 {
391 dt_node_t *stmt;
392 dt_node_t *prev_stmt = NULL;
393 dt_node_t *next_stmt;
394 dt_node_t *first_stmt_in_basic_block = NULL;
395
396 for (stmt = stmts; stmt != NULL; stmt = next_stmt) {
397 next_stmt = stmt->dn_list;
398
399 if (stmt->dn_kind != DT_NODE_IF) {
400 if (first_stmt_in_basic_block == NULL)
401 first_stmt_in_basic_block = stmt;
402 prev_stmt = stmt;
403 continue;
404 }
405
406 /*
407 * Remove this and following statements from the previous
408 * clause.
409 */
410 if (prev_stmt != NULL)
411 prev_stmt->dn_list = NULL;
412
413 /*
414 * Generate clause for statements preceding the "if"
415 */
416 if (first_stmt_in_basic_block != NULL) {
417 xd_new_basic_block(dp, precondition,
418 first_stmt_in_basic_block);
419 }
420
421 xd_do_if(dp, stmt, precondition);
422
423 first_stmt_in_basic_block = NULL;
424
425 prev_stmt = stmt;
426 }
427
428 /* generate clause for statements after last "if". */
429 if (first_stmt_in_basic_block != NULL)
430 xd_new_basic_block(dp, precondition, first_stmt_in_basic_block);
431 }
432
433 /*
434 * Generate a new clause which will set the error variable when an error occurs.
435 * Only one of these clauses is created per program (e.g. script file).
436 * The clause is:
437 *
438 * dtrace:::ERROR{ self->%error = 1; }
439 */
440 static dt_node_t *
441 xd_makeerrorclause(void)
442 {
443 dt_node_t *acts, *pdesc;
444
445 pdesc = dt_node_pdesc_by_name(strdup("dtrace:::ERROR"));
446
447 acts = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
448 xd_new_error_var(), dt_node_int(1)));
449
450 return (dt_node_clause(pdesc, NULL, acts));
451 }
452
453 /*
454 * Transform the super-clause into straight-D, returning the new list of
455 * sub-clauses.
456 */
457 dt_node_t *
458 dt_compile_sugar(dtrace_hdl_t *dtp, dt_node_t *clause)
459 {
460 xd_parse_t dp = { 0 };
461 int condid = 0;
462
463 dp.xp_dtp = dtp;
464 dp.xp_pdescs = clause->dn_pdescs;
465
466 /* make dt_node_int() generate an "int"-typed integer */
467 yyintdecimal = B_TRUE;
468 yyintsuffix[0] = '\0';
469 yyintprefix = 0;
470
471 xd_visit_all(&dp, clause);
472
473 if (dp.xp_num_ifs == 0 && dp.xp_num_conditions == 0) {
474 /*
475 * There is nothing that modifies the number of clauses.
476 * Use the existing clause as-is, with its predicate intact.
477 * This ensures that in the absence of D++, the body of the
478 * clause can create a variable that is referenced in the
479 * predicate.
480 */
481 xd_append_clause(&dp, dt_node_clause(clause->dn_pdescs,
482 clause->dn_pred, clause->dn_acts));
483 } else {
484 if (clause->dn_pred != NULL)
485 condid = xd_new_condition(&dp, clause->dn_pred, condid);
486
487 if (clause->dn_acts == NULL) {
488 /*
489 * xd_visit_stmts() does not emit a clause with
490 * an empty body (e.g. if there's an empty "if" body),
491 * but we need the empty body here so that we
492 * continue to get the default tracing action.
493 */
494 xd_new_basic_block(&dp, condid, NULL);
495 } else {
496 xd_visit_stmts(&dp, clause->dn_acts, condid);
497 }
498 }
499
500 if (dp.xp_num_conditions != 0)
501 xd_prepend_clause(&dp, xd_new_clearerror_clause(&dp));
502 if (dp.xp_clause_list != NULL &&
503 dp.xp_clause_list->dn_list != NULL && !dtp->dt_has_sugar) {
504 dtp->dt_has_sugar = B_TRUE;
505 xd_prepend_clause(&dp, xd_makeerrorclause());
506 }
507 return (dp.xp_clause_list);
508 }