Home | History | Annotate | Line # | Download | only in common
      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