Print this page
7085 add support for "if" and "else" statements in dtrace

@@ -19,12 +19,12 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
  * Copyright (c) 2013, Joyent Inc. All rights reserved.
- * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
 /*
  * DTrace D Language Parser
  *

@@ -2135,10 +2135,21 @@
         dnp->dn_expr = expr;
         return (dnp);
 }
 
 dt_node_t *
+dt_node_if(dt_node_t *pred, dt_node_t *acts, dt_node_t *else_acts)
+{
+        dt_node_t *dnp = dt_node_alloc(DT_NODE_IF);
+        dnp->dn_conditional = pred;
+        dnp->dn_body = acts;
+        dnp->dn_alternate_body = else_acts;
+
+        return (dnp);
+}
+
+dt_node_t *
 dt_node_pdesc_by_name(char *spec)
 {
         dtrace_hdl_t *dtp = yypcb->pcb_hdl;
         dt_node_t *dnp;
 

@@ -2203,11 +2214,10 @@
 
         dnp->dn_pdescs = pdescs;
         dnp->dn_pred = pred;
         dnp->dn_acts = acts;
 
-        yybegin(YYS_CLAUSE);
         return (dnp);
 }
 
 dt_node_t *
 dt_node_inline(dt_node_t *expr)

@@ -3195,12 +3205,13 @@
 
                         if (idp == NULL || dt_ident_unref(idp))
                                 dt_xcook_ident(lp, dhp, idkind, B_TRUE);
                         else
                                 dt_xcook_ident(lp, dhp, idp->di_kind, B_FALSE);
-                } else
+                } else {
                         lp = dnp->dn_left = dt_node_cook(lp, 0);
+                }
 
                 /*
                  * Switch op to '+' for *(E1 + E2) array mode in these cases:
                  * (a) lp is a DT_IDENT_ARRAY variable that has already been
                  *      referenced using [] notation (dn_args != NULL).

@@ -3210,15 +3221,17 @@
                  */
                 if (lp->dn_kind == DT_NODE_VAR) {
                         if (lp->dn_ident->di_kind == DT_IDENT_ARRAY) {
                                 if (lp->dn_args != NULL)
                                         op = DT_TOK_ADD;
-                        } else if (!dt_ident_unref(lp->dn_ident))
+                        } else if (!dt_ident_unref(lp->dn_ident)) {
                                 op = DT_TOK_ADD;
-                } else if (lp->dn_kind != DT_NODE_AGG)
+                        }
+                } else if (lp->dn_kind != DT_NODE_AGG) {
                         op = DT_TOK_ADD;
         }
+        }
 
         switch (op) {
         case DT_TOK_BAND:
         case DT_TOK_XOR:
         case DT_TOK_BOR:

@@ -3637,49 +3650,38 @@
                 dt_assign_common(dnp);
                 break;
 
         case DT_TOK_PTR:
                 /*
-                 * If the left-hand side of operator -> is the name "self",
-                 * then we permit a TLS variable to be created or referenced.
+                 * If the left-hand side of operator -> is one of the
+                 * scoping keywords, permit a local or thread
+                 * variable to be created or referenced.
                  */
-                if (lp->dn_kind == DT_NODE_IDENT &&
-                    strcmp(lp->dn_string, "self") == 0) {
-                        if (rp->dn_kind != DT_NODE_VAR) {
-                                dt_xcook_ident(rp, dtp->dt_tls,
-                                    DT_IDENT_SCALAR, B_TRUE);
-                        }
+                if (lp->dn_kind == DT_NODE_IDENT) {
+                        dt_idhash_t *dhp = NULL;
 
-                        if (idflags != 0)
-                                rp = dt_node_cook(rp, idflags);
-
-                        dnp->dn_right = dnp->dn_left; /* avoid freeing rp */
-                        dt_node_free(dnp);
-                        return (rp);
+                        if (strcmp(lp->dn_string, "self") == 0) {
+                                dhp = dtp->dt_tls;
+                        } else if (strcmp(lp->dn_string, "this") == 0) {
+                                dhp = yypcb->pcb_locals;
                 }
-
-                /*
-                 * If the left-hand side of operator -> is the name "this",
-                 * then we permit a local variable to be created or referenced.
-                 */
-                if (lp->dn_kind == DT_NODE_IDENT &&
-                    strcmp(lp->dn_string, "this") == 0) {
+                        if (dhp != NULL) {
                         if (rp->dn_kind != DT_NODE_VAR) {
-                                dt_xcook_ident(rp, yypcb->pcb_locals,
+                                        dt_xcook_ident(rp, dhp,
                                     DT_IDENT_SCALAR, B_TRUE);
                         }
 
                         if (idflags != 0)
                                 rp = dt_node_cook(rp, idflags);
 
-                        dnp->dn_right = dnp->dn_left; /* avoid freeing rp */
+                                /* avoid freeing rp */
+                                dnp->dn_right = dnp->dn_left;
                         dt_node_free(dnp);
                         return (rp);
                 }
-
+                }
                 /*FALLTHRU*/
-
         case DT_TOK_DOT:
                 lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF);
 
                 if (rp->dn_kind != DT_NODE_IDENT) {
                         xyerror(D_OP_IDENT, "operator %s must be followed by "

@@ -4494,11 +4496,12 @@
         dt_cook_inline,         /* DT_NODE_INLINE */
         dt_cook_member,         /* DT_NODE_MEMBER */
         dt_cook_xlator,         /* DT_NODE_XLATOR */
         dt_cook_none,           /* DT_NODE_PROBE */
         dt_cook_provider,       /* DT_NODE_PROVIDER */
-        dt_cook_none            /* DT_NODE_PROG */
+        dt_cook_none,           /* DT_NODE_PROG */
+        dt_cook_none,           /* DT_NODE_IF */
 };
 
 /*
  * Recursively cook the parse tree starting at the specified node.  The idflags
  * parameter is used to indicate the type of reference (r/w) and is applied to

@@ -4509,10 +4512,12 @@
 {
         int oldlineno = yylineno;
 
         yylineno = dnp->dn_line;
 
+        assert(dnp->dn_kind <
+            sizeof (dt_cook_funcs) / sizeof (dt_cook_funcs[0]));
         dnp = dt_cook_funcs[dnp->dn_kind](dnp, idflags);
         dnp->dn_flags |= DT_NF_COOKED;
 
         if (dnp->dn_kind == DT_NODE_VAR || dnp->dn_kind == DT_NODE_AGG)
                 dnp->dn_ident->di_flags |= idflags;

@@ -4611,11 +4616,186 @@
             DIF_TF_BYREF : 0;
         tp->dtdt_pad = 0;
         tp->dtdt_size = ctf_type_size(dnp->dn_ctfp, dnp->dn_type);
 }
 
+/*
+ * Output the parse tree as D.  The "-xtree=8" argument will call this
+ * function to print out the program after any syntactic sugar
+ * transformations have been applied (e.g. to implement "if").  The
+ * resulting output can be used to understand the transformations
+ * applied by these features, or to run such a script on a system that
+ * does not support these features
+ *
+ * Note that the output does not express precisely the same program as
+ * the input.  In particular:
+ *  - Only the clauses are output.  #pragma options, variable
+ *    declarations, etc. are excluded.
+ *  - Command argument substitution has already been done, so the output
+ *    will not contain e.g. $$1, but rather the substituted string.
+ */
 void
+dt_printd(dt_node_t *dnp, FILE *fp, int depth)
+{
+        dt_node_t *arg;
+
+        switch (dnp->dn_kind) {
+        case DT_NODE_INT:
+                (void) fprintf(fp, "0x%llx", (u_longlong_t)dnp->dn_value);
+                if (!(dnp->dn_flags & DT_NF_SIGNED))
+                        (void) fprintf(fp, "u");
+                break;
+
+        case DT_NODE_STRING: {
+                char *escd = strchr2esc(dnp->dn_string, strlen(dnp->dn_string));
+                (void) fprintf(fp, "\"%s\"", escd);
+                free(escd);
+                break;
+        }
+
+        case DT_NODE_IDENT:
+                (void) fprintf(fp, "%s", dnp->dn_string);
+                break;
+
+        case DT_NODE_VAR:
+                (void) fprintf(fp, "%s%s",
+                    (dnp->dn_ident->di_flags & DT_IDFLG_LOCAL) ? "this->" :
+                    (dnp->dn_ident->di_flags & DT_IDFLG_TLS) ? "self->" : "",
+                    dnp->dn_ident->di_name);
+
+                if (dnp->dn_args != NULL) {
+                        (void) fprintf(fp, "[");
+
+                        for (arg = dnp->dn_args; arg != NULL;
+                            arg = arg->dn_list) {
+                                dt_printd(arg, fp, 0);
+                                if (arg->dn_list != NULL)
+                                        (void) fprintf(fp, ", ");
+                        }
+
+                        (void) fprintf(fp, "]");
+                }
+                break;
+
+        case DT_NODE_SYM: {
+                const dtrace_syminfo_t *dts = dnp->dn_ident->di_data;
+                (void) fprintf(fp, "%s`%s", dts->dts_object, dts->dts_name);
+                break;
+        }
+        case DT_NODE_FUNC:
+                (void) fprintf(fp, "%s(", dnp->dn_ident->di_name);
+
+                for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list) {
+                        dt_printd(arg, fp, 0);
+                        if (arg->dn_list != NULL)
+                                (void) fprintf(fp, ", ");
+                }
+                (void) fprintf(fp, ")");
+                break;
+
+        case DT_NODE_OP1:
+                (void) fprintf(fp, "%s(", opstr(dnp->dn_op));
+                dt_printd(dnp->dn_child, fp, 0);
+                (void) fprintf(fp, ")");
+                break;
+
+        case DT_NODE_OP2:
+                (void) fprintf(fp, "(");
+                dt_printd(dnp->dn_left, fp, 0);
+                if (dnp->dn_op == DT_TOK_LPAR) {
+                        (void) fprintf(fp, ")");
+                        dt_printd(dnp->dn_right, fp, 0);
+                        break;
+                }
+                if (dnp->dn_op == DT_TOK_PTR || dnp->dn_op == DT_TOK_DOT ||
+                    dnp->dn_op == DT_TOK_LBRAC)
+                        (void) fprintf(fp, "%s", opstr(dnp->dn_op));
+                else
+                        (void) fprintf(fp, " %s ", opstr(dnp->dn_op));
+                dt_printd(dnp->dn_right, fp, 0);
+                if (dnp->dn_op == DT_TOK_LBRAC) {
+                        dt_node_t *ln = dnp->dn_right;
+                        while (ln->dn_list != NULL) {
+                                (void) fprintf(fp, ", ");
+                                dt_printd(ln->dn_list, fp, depth);
+                                ln = ln->dn_list;
+                        }
+                        (void) fprintf(fp, "]");
+                }
+                (void) fprintf(fp, ")");
+                break;
+
+        case DT_NODE_OP3:
+                (void) fprintf(fp, "(");
+                dt_printd(dnp->dn_expr, fp, 0);
+                (void) fprintf(fp, " ? ");
+                dt_printd(dnp->dn_left, fp, 0);
+                (void) fprintf(fp, " : ");
+                dt_printd(dnp->dn_right, fp, 0);
+                (void) fprintf(fp, ")");
+                break;
+
+        case DT_NODE_DEXPR:
+        case DT_NODE_DFUNC:
+                (void) fprintf(fp, "%*s", depth * 8, "");
+                dt_printd(dnp->dn_expr, fp, depth + 1);
+                (void) fprintf(fp, ";\n");
+                break;
+
+        case DT_NODE_PDESC:
+                (void) fprintf(fp, "%s:%s:%s:%s",
+                    dnp->dn_desc->dtpd_provider, dnp->dn_desc->dtpd_mod,
+                    dnp->dn_desc->dtpd_func, dnp->dn_desc->dtpd_name);
+                break;
+
+        case DT_NODE_CLAUSE:
+                for (arg = dnp->dn_pdescs; arg != NULL; arg = arg->dn_list) {
+                        dt_printd(arg, fp, 0);
+                        if (arg->dn_list != NULL)
+                                (void) fprintf(fp, ",");
+                        (void) fprintf(fp, "\n");
+                }
+
+                if (dnp->dn_pred != NULL) {
+                        (void) fprintf(fp, "/");
+                        dt_printd(dnp->dn_pred, fp, 0);
+                        (void) fprintf(fp, "/\n");
+                }
+                        (void) fprintf(fp, "{\n");
+
+                for (arg = dnp->dn_acts; arg != NULL; arg = arg->dn_list)
+                        dt_printd(arg, fp, depth + 1);
+                (void) fprintf(fp, "}\n");
+                (void) fprintf(fp, "\n");
+                break;
+
+        case DT_NODE_IF:
+                (void) fprintf(fp, "%*sif (", depth * 8, "");
+                dt_printd(dnp->dn_conditional, fp, 0);
+                (void) fprintf(fp, ") {\n");
+
+                for (arg = dnp->dn_body; arg != NULL; arg = arg->dn_list)
+                        dt_printd(arg, fp, depth + 1);
+                if (dnp->dn_alternate_body == NULL) {
+                        (void) fprintf(fp, "%*s}\n", depth * 8, "");
+                } else {
+                        (void) fprintf(fp, "%*s} else {\n", depth * 8, "");
+                        for (arg = dnp->dn_alternate_body; arg != NULL;
+                            arg = arg->dn_list)
+                                dt_printd(arg, fp, depth + 1);
+                        (void) fprintf(fp, "%*s}\n", depth * 8, "");
+                }
+
+                break;
+
+        default:
+                (void) fprintf(fp, "/* bad node %p, kind %d */\n",
+                    (void *)dnp, dnp->dn_kind);
+        }
+}
+
+void
 dt_node_printr(dt_node_t *dnp, FILE *fp, int depth)
 {
         char n[DT_TYPE_NAMELEN], buf[BUFSIZ], a[8];
         const dtrace_syminfo_t *dts;
         const dt_idnode_t *inp;

@@ -4721,10 +4901,17 @@
 
         case DT_NODE_OP2:
                 (void) fprintf(fp, "OP2 %s (%s)\n", opstr(dnp->dn_op), buf);
                 dt_node_printr(dnp->dn_left, fp, depth + 1);
                 dt_node_printr(dnp->dn_right, fp, depth + 1);
+                if (dnp->dn_op == DT_TOK_LBRAC) {
+                        dt_node_t *ln = dnp->dn_right;
+                        while (ln->dn_list != NULL) {
+                                dt_node_printr(ln->dn_list, fp, depth + 1);
+                                ln = ln->dn_list;
+                        }
+                }
                 break;
 
         case DT_NODE_OP3:
                 (void) fprintf(fp, "OP3 (%s)\n", buf);
                 dt_node_printr(dnp->dn_expr, fp, depth + 1);

@@ -4782,10 +4969,11 @@
                         (void) fprintf(fp, "%*s/\n", depth * 2, "");
                 }
 
                 for (arg = dnp->dn_acts; arg != NULL; arg = arg->dn_list)
                         dt_node_printr(arg, fp, depth + 1);
+                (void) fprintf(fp, "\n");
                 break;
 
         case DT_NODE_INLINE:
                 inp = dnp->dn_ident->di_iarg;
 

@@ -4832,10 +5020,28 @@
                 (void) fprintf(fp, "PROGRAM attr=%s\n", a);
                 for (arg = dnp->dn_list; arg != NULL; arg = arg->dn_list)
                         dt_node_printr(arg, fp, depth + 1);
                 break;
 
+        case DT_NODE_IF:
+                (void) fprintf(fp, "IF attr=%s CONDITION:\n", a);
+
+                dt_node_printr(dnp->dn_conditional, fp, depth + 1);
+
+                (void) fprintf(fp, "%*sIF BODY: \n", depth * 2, "");
+                for (arg = dnp->dn_body; arg != NULL; arg = arg->dn_list)
+                        dt_node_printr(arg, fp, depth + 1);
+
+                if (dnp->dn_alternate_body != NULL) {
+                        (void) fprintf(fp, "%*sIF ELSE: \n", depth * 2, "");
+                        for (arg = dnp->dn_alternate_body; arg != NULL;
+                            arg = arg->dn_list)
+                                dt_node_printr(arg, fp, depth + 1);
+                }
+
+                break;
+
         default:
                 (void) fprintf(fp, "<bad node %p, kind %d>\n",
                     (void *)dnp, dnp->dn_kind);
         }
 }