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 * Syntactic sugar features are implemented by transforming the D parse tree 22 * such that it only uses the subset of D that is supported by the rest of the 23 * compiler / the kernel. A clause containing these language features is 24 * referred to as a "super-clause", and its transformation typically entails 25 * creating several "sub-clauses" to implement it. For diagnosability, the 26 * sub-clauses will be printed if the "-xtree=8" flag is specified. 27 * 28 * Currently, the only syntactic sugar feature is "if/else" statements. Each 29 * basic block (e.g. the body of the "if" and "else" statements, and the 30 * statements before and after) is turned into its own sub-clause, with a 31 * predicate that causes it to be executed only if the code flows to this point. 32 * Nested if/else statements are supported. 33 * 34 * This infrastructure is designed to accommodate other syntactic sugar features 35 * in the future. 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 dt_sugar_parse { 58 dtrace_hdl_t *dtsp_dtp; /* dtrace handle */ 59 dt_node_t *dtsp_pdescs; /* probe descriptions */ 60 int dtsp_num_conditions; /* number of condition variables */ 61 int dtsp_num_ifs; /* number of "if" statements */ 62 dt_node_t *dtsp_clause_list; /* list of clauses */ 63 } dt_sugar_parse_t; 64 65 static void dt_sugar_visit_stmts(dt_sugar_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 dt_sugar_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 dt_sugar_append_clause(dt_sugar_parse_t *dp, dt_node_t *clause) 89 { 90 dp->dtsp_clause_list = dt_node_link(dp->dtsp_clause_list, clause); 91 } 92 93 /* 94 * Prepend this clause to the clause list. 95 */ 96 static void 97 dt_sugar_prepend_clause(dt_sugar_parse_t *dp, dt_node_t *clause) 98 { 99 dp->dtsp_clause_list = dt_node_link(clause, dp->dtsp_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 dt_sugar_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 dt_sugar_new_condition_impl(dt_sugar_parse_t *dp, 143 dt_node_t *pred, int condid, int newcond) 144 { 145 dt_node_t *value, *body, *newpred; 146 147 /* predicate is !self->%error */ 148 newpred = dt_node_op1(DT_TOK_LNEG, dt_sugar_new_error_var()); 149 150 if (condid == 0) { 151 /* 152 * value is (1 && pred) 153 * 154 * Note, D doesn't allow a probe-local "this" variable to 155 * be reused as a different type, even from a different probe. 156 * Therefore, value can't simply be <pred>, because then 157 * its type could be different when we reuse this condid 158 * in a different meta-clause. 159 */ 160 value = dt_node_op2(DT_TOK_LAND, dt_node_int(1), pred); 161 } else { 162 /* value is (this->%condition_<condid> && pred) */ 163 value = dt_node_op2(DT_TOK_LAND, 164 dt_sugar_new_condition_var(condid), pred); 165 } 166 167 /* body is "this->%condition_<retval> = <value>;" */ 168 body = dt_node_statement(dt_node_op2(DT_TOK_ASGN, 169 dt_sugar_new_condition_var(newcond), value)); 170 171 return (dt_node_clause(dp->dtsp_pdescs, newpred, body)); 172 } 173 174 /* 175 * Generate a new clause to evaluate predicate and set a new condition variable, 176 * whose ID will be returned. The new clause will be appended to 177 * dp_first_new_clause. 178 */ 179 static int 180 dt_sugar_new_condition(dt_sugar_parse_t *dp, dt_node_t *pred, int condid) 181 { 182 dp->dtsp_num_conditions++; 183 dt_sugar_append_clause(dp, dt_sugar_new_condition_impl(dp, 184 pred, condid, dp->dtsp_num_conditions)); 185 return (dp->dtsp_num_conditions); 186 } 187 188 /* 189 * Visit the specified node and all of its descendants. Currently this is only 190 * used to count the number of "if" statements (dtsp_num_ifs). 191 */ 192 static void 193 dt_sugar_visit_all(dt_sugar_parse_t *dp, dt_node_t *dnp) 194 { 195 dt_node_t *arg; 196 197 switch (dnp->dn_kind) { 198 case DT_NODE_FREE: 199 case DT_NODE_INT: 200 case DT_NODE_STRING: 201 case DT_NODE_SYM: 202 case DT_NODE_TYPE: 203 case DT_NODE_PROBE: 204 case DT_NODE_PDESC: 205 case DT_NODE_IDENT: 206 break; 207 208 case DT_NODE_FUNC: 209 for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list) 210 dt_sugar_visit_all(dp, arg); 211 break; 212 213 case DT_NODE_OP1: 214 dt_sugar_visit_all(dp, dnp->dn_child); 215 break; 216 217 case DT_NODE_OP2: 218 dt_sugar_visit_all(dp, dnp->dn_left); 219 dt_sugar_visit_all(dp, dnp->dn_right); 220 if (dnp->dn_op == DT_TOK_LBRAC) { 221 dt_node_t *ln = dnp->dn_right; 222 while (ln->dn_list != NULL) { 223 dt_sugar_visit_all(dp, ln->dn_list); 224 ln = ln->dn_list; 225 } 226 } 227 break; 228 229 case DT_NODE_OP3: 230 dt_sugar_visit_all(dp, dnp->dn_expr); 231 dt_sugar_visit_all(dp, dnp->dn_left); 232 dt_sugar_visit_all(dp, dnp->dn_right); 233 break; 234 235 case DT_NODE_DEXPR: 236 case DT_NODE_DFUNC: 237 dt_sugar_visit_all(dp, dnp->dn_expr); 238 break; 239 240 case DT_NODE_AGG: 241 for (arg = dnp->dn_aggtup; arg != NULL; arg = arg->dn_list) 242 dt_sugar_visit_all(dp, arg); 243 244 if (dnp->dn_aggfun) 245 dt_sugar_visit_all(dp, dnp->dn_aggfun); 246 break; 247 248 case DT_NODE_CLAUSE: 249 for (arg = dnp->dn_pdescs; arg != NULL; arg = arg->dn_list) 250 dt_sugar_visit_all(dp, arg); 251 252 if (dnp->dn_pred != NULL) 253 dt_sugar_visit_all(dp, dnp->dn_pred); 254 255 for (arg = dnp->dn_acts; arg != NULL; arg = arg->dn_list) 256 dt_sugar_visit_all(dp, arg); 257 break; 258 259 case DT_NODE_INLINE: { 260 const dt_idnode_t *inp = dnp->dn_ident->di_iarg; 261 262 dt_sugar_visit_all(dp, inp->din_root); 263 break; 264 } 265 case DT_NODE_MEMBER: 266 if (dnp->dn_membexpr) 267 dt_sugar_visit_all(dp, dnp->dn_membexpr); 268 break; 269 270 case DT_NODE_XLATOR: 271 for (arg = dnp->dn_members; arg != NULL; arg = arg->dn_list) 272 dt_sugar_visit_all(dp, arg); 273 break; 274 275 case DT_NODE_PROVIDER: 276 for (arg = dnp->dn_probes; arg != NULL; arg = arg->dn_list) 277 dt_sugar_visit_all(dp, arg); 278 break; 279 280 case DT_NODE_PROG: 281 for (arg = dnp->dn_list; arg != NULL; arg = arg->dn_list) 282 dt_sugar_visit_all(dp, arg); 283 break; 284 285 case DT_NODE_IF: 286 dp->dtsp_num_ifs++; 287 dt_sugar_visit_all(dp, dnp->dn_conditional); 288 289 for (arg = dnp->dn_body; arg != NULL; arg = arg->dn_list) 290 dt_sugar_visit_all(dp, arg); 291 for (arg = dnp->dn_alternate_body; arg != NULL; 292 arg = arg->dn_list) 293 dt_sugar_visit_all(dp, arg); 294 295 break; 296 297 default: 298 (void) dnerror(dnp, D_UNKNOWN, "bad node %p, kind %d\n", 299 (void *)dnp, dnp->dn_kind); 300 } 301 } 302 303 /* 304 * Return a new clause which resets the error variable to zero: 305 * 306 * dp_pdescs{ self->%error = 0; } 307 * 308 * This clause will be executed at the beginning of each meta-clause, to 309 * ensure the error variable is unset (in case the previous meta-clause 310 * failed). 311 */ 312 static dt_node_t * 313 dt_sugar_new_clearerror_clause(dt_sugar_parse_t *dp) 314 { 315 dt_node_t *stmt = dt_node_statement(dt_node_op2(DT_TOK_ASGN, 316 dt_sugar_new_error_var(), dt_node_int(0))); 317 return (dt_node_clause(dp->dtsp_pdescs, NULL, stmt)); 318 } 319 320 /* 321 * Evaluate the conditional, and recursively visit the body of the "if" 322 * statement (and the "else", if present). 323 */ 324 static void 325 dt_sugar_do_if(dt_sugar_parse_t *dp, dt_node_t *if_stmt, int precondition) 326 { 327 int newid; 328 329 assert(if_stmt->dn_kind == DT_NODE_IF); 330 331 /* condition */ 332 newid = dt_sugar_new_condition(dp, 333 if_stmt->dn_conditional, precondition); 334 335 /* body of if */ 336 dt_sugar_visit_stmts(dp, if_stmt->dn_body, newid); 337 338 /* 339 * Visit the body of the "else" statement, if present. Note that we 340 * generate a new condition which is the inverse of the previous 341 * condition. 342 */ 343 if (if_stmt->dn_alternate_body != NULL) { 344 dt_node_t *pred = 345 dt_node_op1(DT_TOK_LNEG, dt_sugar_new_condition_var(newid)); 346 dt_sugar_visit_stmts(dp, if_stmt->dn_alternate_body, 347 dt_sugar_new_condition(dp, pred, precondition)); 348 } 349 } 350 351 /* 352 * Generate a new clause to evaluate the statements based on the condition. 353 * The new clause will be appended to dp_first_new_clause. 354 * 355 * dp_pdescs 356 * /!self->%error && this->%condition_<condid>/ 357 * { 358 * stmts 359 * } 360 */ 361 static void 362 dt_sugar_new_basic_block(dt_sugar_parse_t *dp, int condid, dt_node_t *stmts) 363 { 364 dt_node_t *pred = NULL; 365 366 if (condid == 0) { 367 /* 368 * Don't bother with !error on the first clause, because if 369 * there is only one clause, we don't add the prelude to 370 * zero out %error. 371 */ 372 if (dp->dtsp_num_conditions != 0) { 373 pred = dt_node_op1(DT_TOK_LNEG, 374 dt_sugar_new_error_var()); 375 } 376 } else { 377 pred = dt_node_op2(DT_TOK_LAND, 378 dt_node_op1(DT_TOK_LNEG, dt_sugar_new_error_var()), 379 dt_sugar_new_condition_var(condid)); 380 } 381 dt_sugar_append_clause(dp, 382 dt_node_clause(dp->dtsp_pdescs, pred, stmts)); 383 } 384 385 /* 386 * Visit all the statements in this list, and break them into basic blocks, 387 * generating new clauses for "if" and "else" statements. 388 */ 389 static void 390 dt_sugar_visit_stmts(dt_sugar_parse_t *dp, dt_node_t *stmts, int precondition) 391 { 392 dt_node_t *stmt; 393 dt_node_t *prev_stmt = NULL; 394 dt_node_t *next_stmt; 395 dt_node_t *first_stmt_in_basic_block = NULL; 396 397 for (stmt = stmts; stmt != NULL; stmt = next_stmt) { 398 next_stmt = stmt->dn_list; 399 400 if (stmt->dn_kind != DT_NODE_IF) { 401 if (first_stmt_in_basic_block == NULL) 402 first_stmt_in_basic_block = stmt; 403 prev_stmt = stmt; 404 continue; 405 } 406 407 /* 408 * Remove this and following statements from the previous 409 * clause. 410 */ 411 if (prev_stmt != NULL) 412 prev_stmt->dn_list = NULL; 413 414 /* 415 * Generate clause for statements preceding the "if" 416 */ 417 if (first_stmt_in_basic_block != NULL) { 418 dt_sugar_new_basic_block(dp, precondition, 419 first_stmt_in_basic_block); 420 } 421 422 dt_sugar_do_if(dp, stmt, precondition); 423 424 first_stmt_in_basic_block = NULL; 425 426 prev_stmt = stmt; 427 } 428 429 /* generate clause for statements after last "if". */ 430 if (first_stmt_in_basic_block != NULL) { 431 dt_sugar_new_basic_block(dp, precondition, 432 first_stmt_in_basic_block); 433 } 434 } 435 436 /* 437 * Generate a new clause which will set the error variable when an error occurs. 438 * Only one of these clauses is created per program (e.g. script file). 439 * The clause is: 440 * 441 * dtrace:::ERROR{ self->%error = 1; } 442 */ 443 static dt_node_t * 444 dt_sugar_makeerrorclause(void) 445 { 446 dt_node_t *acts, *pdesc; 447 448 pdesc = dt_node_pdesc_by_name(strdup("dtrace:::ERROR")); 449 450 acts = dt_node_statement(dt_node_op2(DT_TOK_ASGN, 451 dt_sugar_new_error_var(), dt_node_int(1))); 452 453 return (dt_node_clause(pdesc, NULL, acts)); 454 } 455 456 /* 457 * Transform the super-clause into straight-D, returning the new list of 458 * sub-clauses. 459 */ 460 dt_node_t * 461 dt_compile_sugar(dtrace_hdl_t *dtp, dt_node_t *clause) 462 { 463 dt_sugar_parse_t dp = { 0 }; 464 int condid = 0; 465 466 dp.dtsp_dtp = dtp; 467 dp.dtsp_pdescs = clause->dn_pdescs; 468 469 /* make dt_node_int() generate an "int"-typed integer */ 470 yyintdecimal = B_TRUE; 471 yyintsuffix[0] = '\0'; 472 yyintprefix = 0; 473 474 dt_sugar_visit_all(&dp, clause); 475 476 if (dp.dtsp_num_ifs == 0 && dp.dtsp_num_conditions == 0) { 477 /* 478 * There is nothing that modifies the number of clauses. Use 479 * the existing clause as-is, with its predicate intact. This 480 * ensures that in the absence of D sugar, the body of the 481 * clause can create a variable that is referenced in the 482 * predicate. 483 */ 484 dt_sugar_append_clause(&dp, dt_node_clause(clause->dn_pdescs, 485 clause->dn_pred, clause->dn_acts)); 486 } else { 487 if (clause->dn_pred != NULL) { 488 condid = dt_sugar_new_condition(&dp, 489 clause->dn_pred, condid); 490 } 491 492 if (clause->dn_acts == NULL) { 493 /* 494 * dt_sugar_visit_stmts() does not emit a clause with 495 * an empty body (e.g. if there's an empty "if" body), 496 * but we need the empty body here so that we 497 * continue to get the default tracing action. 498 */ 499 dt_sugar_new_basic_block(&dp, condid, NULL); 500 } else { 501 dt_sugar_visit_stmts(&dp, clause->dn_acts, condid); 502 } 503 } 504 505 if (dp.dtsp_num_conditions != 0) { 506 dt_sugar_prepend_clause(&dp, 507 dt_sugar_new_clearerror_clause(&dp)); 508 } 509 510 if (dp.dtsp_clause_list != NULL && 511 dp.dtsp_clause_list->dn_list != NULL && !dtp->dt_has_sugar) { 512 dtp->dt_has_sugar = B_TRUE; 513 dt_sugar_prepend_clause(&dp, dt_sugar_makeerrorclause()); 514 } 515 return (dp.dtsp_clause_list); 516 } 517