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 }