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 }