7085 add support for "if" and "else" statements in dtrace
illumos/illumos-gate@c3bd3abd88 Add syntactic sugar to dtrace: "if" and "else" statements. The sugar is baked down to standard dtrace features by adding additional clauses with the appropriate predicates. Reviewed by: Adam Leventhal <ahl@delphix.com> Reviewed by: Sebastien Roy <sebastien.roy@delphix.com> Reviewed by: Paul Dagnelie <pcd@delphix.com> Reviewed by: Bryan Cantrill <bryan@joyent.com> Approved by: Richard Lowe <richlowe@richlowe.net> Author: Matthew Ahrens <mahrens@delphix.com>
This commit is contained in:
parent
da3d1eecc5
commit
8a411dff07
@ -25,6 +25,10 @@
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
#
|
||||
|
||||
require 5.8.4;
|
||||
|
||||
$PNAME = $0;
|
||||
@ -131,7 +135,8 @@ sub dstyle
|
||||
}
|
||||
|
||||
if (!/^enum/ && !/^\t*struct/ && !/^\t*union/ && !/^typedef/ &&
|
||||
!/^translator/ && !/^provider/) {
|
||||
!/^translator/ && !/^provider/ && !/\tif / &&
|
||||
!/ else /) {
|
||||
if (/[\w\s]+{/) {
|
||||
err "left brace not on its own line";
|
||||
}
|
||||
@ -141,7 +146,7 @@ sub dstyle
|
||||
}
|
||||
}
|
||||
|
||||
if (!/;$/) {
|
||||
if (!/;$/ && !/\t*}$/ && !/ else /) {
|
||||
if (/[\w\s]+}/) {
|
||||
err "right brace not on its own line";
|
||||
}
|
||||
|
33
cmd/dtrace/test/tst/common/sugar/tst.else.d
Normal file
33
cmd/dtrace/test/tst/common/sugar/tst.else.d
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ASSERTION:
|
||||
* "else" statement is executed
|
||||
*/
|
||||
|
||||
BEGIN
|
||||
{
|
||||
if (0) {
|
||||
n = 1;
|
||||
} else {
|
||||
n = 0;
|
||||
}
|
||||
exit(n)
|
||||
}
|
33
cmd/dtrace/test/tst/common/sugar/tst.if.d
Normal file
33
cmd/dtrace/test/tst/common/sugar/tst.if.d
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ASSERTION:
|
||||
* "if" statement executes the correct body.
|
||||
*/
|
||||
|
||||
BEGIN
|
||||
{
|
||||
if (1) {
|
||||
n = 0;
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
exit(n)
|
||||
}
|
33
cmd/dtrace/test/tst/common/sugar/tst.if2.d
Normal file
33
cmd/dtrace/test/tst/common/sugar/tst.if2.d
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ASSERTION:
|
||||
* "if" statement executes the correct body.
|
||||
* parses single-statement, braceless bodies correctly.
|
||||
*/
|
||||
|
||||
BEGIN
|
||||
{
|
||||
if (1)
|
||||
n = 0;
|
||||
else
|
||||
n = 1;
|
||||
exit(n)
|
||||
}
|
46
cmd/dtrace/test/tst/common/sugar/tst.if_before_after.d
Normal file
46
cmd/dtrace/test/tst/common/sugar/tst.if_before_after.d
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ASSERTION:
|
||||
* statements before and after an if statement are executed.
|
||||
*/
|
||||
|
||||
BEGIN
|
||||
{
|
||||
i = 1;
|
||||
if (1) {
|
||||
i++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
BEGIN
|
||||
/i == 3/
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
BEGIN
|
||||
/i != 3/
|
||||
{
|
||||
exit(1);
|
||||
}
|
38
cmd/dtrace/test/tst/common/sugar/tst.if_nested.d
Normal file
38
cmd/dtrace/test/tst/common/sugar/tst.if_nested.d
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ASSERTION:
|
||||
* nested "if" statement executes the correct body.
|
||||
*/
|
||||
|
||||
BEGIN
|
||||
{
|
||||
if (0) {
|
||||
exit(1);
|
||||
} else {
|
||||
if (0) {
|
||||
exit(1);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
exit(1);
|
||||
}
|
30
cmd/dtrace/test/tst/common/sugar/tst.if_trailing_semicolon.d
Normal file
30
cmd/dtrace/test/tst/common/sugar/tst.if_trailing_semicolon.d
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ASSERTION:
|
||||
* "if" body without trailing semicolon parses correctly
|
||||
*/
|
||||
|
||||
BEGIN
|
||||
{
|
||||
if (1) {
|
||||
exit(0)
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ASSERTION:
|
||||
* "if" body without trailing semicolon parses correctly
|
||||
*/
|
||||
|
||||
BEGIN
|
||||
{
|
||||
if (1) {
|
||||
i = 1;
|
||||
exit(0)
|
||||
}
|
||||
}
|
@ -21,8 +21,8 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent Inc. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright 2015 Gary Mills
|
||||
*/
|
||||
|
||||
@ -119,7 +119,6 @@ static const dtrace_diftype_t dt_int_rtype = {
|
||||
static void *dt_compile(dtrace_hdl_t *, int, dtrace_probespec_t, void *,
|
||||
uint_t, int, char *const[], FILE *, const char *);
|
||||
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
dt_idreset(dt_idhash_t *dhp, dt_ident_t *idp, void *ignored)
|
||||
@ -2418,6 +2417,28 @@ dt_compile(dtrace_hdl_t *dtp, int context, dtrace_probespec_t pspec, void *arg,
|
||||
"not referenced)\n", yypcb->pcb_sargv[argc - 1], argc - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform sugar transformations (for "if" / "else") and replace the
|
||||
* existing clause chain with the new one.
|
||||
*/
|
||||
if (context == DT_CTX_DPROG) {
|
||||
dt_node_t *dnp, *next_dnp;
|
||||
dt_node_t *new_list = NULL;
|
||||
|
||||
for (dnp = yypcb->pcb_root->dn_list;
|
||||
dnp != NULL; dnp = next_dnp) {
|
||||
/* remove this node from the list */
|
||||
next_dnp = dnp->dn_list;
|
||||
dnp->dn_list = NULL;
|
||||
|
||||
if (dnp->dn_kind == DT_NODE_CLAUSE)
|
||||
dnp = dt_compile_sugar(dtp, dnp);
|
||||
/* append node to the new list */
|
||||
new_list = dt_node_link(new_list, dnp);
|
||||
}
|
||||
yypcb->pcb_root->dn_list = new_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have successfully created a parse tree for a D program, loop
|
||||
* over the clauses and actions and instantiate the corresponding
|
||||
@ -2438,6 +2459,8 @@ dt_compile(dtrace_hdl_t *dtp, int context, dtrace_probespec_t pspec, void *arg,
|
||||
for (; dnp != NULL; dnp = dnp->dn_list) {
|
||||
switch (dnp->dn_kind) {
|
||||
case DT_NODE_CLAUSE:
|
||||
if (DT_TREEDUMP_PASS(dtp, 4))
|
||||
dt_printd(dnp, stderr, 0);
|
||||
dt_compile_clause(dtp, dnp);
|
||||
break;
|
||||
case DT_NODE_XLATOR:
|
||||
|
@ -23,8 +23,9 @@
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
@ -155,6 +156,8 @@
|
||||
%type <l_node> probe_specifier_list
|
||||
%type <l_node> probe_specifier
|
||||
%type <l_node> statement_list
|
||||
%type <l_node> statement_list_impl
|
||||
%type <l_node> statement_or_block
|
||||
%type <l_node> statement
|
||||
%type <l_node> declaration
|
||||
%type <l_node> init_declarator_list
|
||||
@ -317,9 +320,11 @@ probe_definition:
|
||||
"or actions following probe description\n");
|
||||
}
|
||||
$$ = dt_node_clause($1, NULL, NULL);
|
||||
yybegin(YYS_CLAUSE);
|
||||
}
|
||||
| probe_specifiers '{' statement_list '}' {
|
||||
$$ = dt_node_clause($1, NULL, $3);
|
||||
yybegin(YYS_CLAUSE);
|
||||
}
|
||||
| probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED {
|
||||
dnerror($3, D_SYNTAX, "expected actions { } following "
|
||||
@ -328,6 +333,7 @@ probe_definition:
|
||||
| probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED
|
||||
'{' statement_list '}' {
|
||||
$$ = dt_node_clause($1, $3, $6);
|
||||
yybegin(YYS_CLAUSE);
|
||||
}
|
||||
;
|
||||
|
||||
@ -347,12 +353,30 @@ probe_specifier:
|
||||
| DT_TOK_INT { $$ = dt_node_pdesc_by_id($1); }
|
||||
;
|
||||
|
||||
statement_list: statement { $$ = $1; }
|
||||
| statement_list ';' statement { $$ = LINK($1, $3); }
|
||||
statement_list_impl: /* empty */ { $$ = NULL; }
|
||||
| statement_list_impl statement { $$ = LINK($1, $2); }
|
||||
;
|
||||
|
||||
statement: /* empty */ { $$ = NULL; }
|
||||
| expression { $$ = dt_node_statement($1); }
|
||||
statement_list:
|
||||
statement_list_impl { $$ = $1; }
|
||||
| statement_list_impl expression {
|
||||
$$ = LINK($1, dt_node_statement($2));
|
||||
}
|
||||
;
|
||||
|
||||
statement_or_block:
|
||||
statement
|
||||
| '{' statement_list '}' { $$ = $2; }
|
||||
|
||||
statement: ';' { $$ = NULL; }
|
||||
| expression ';' { $$ = dt_node_statement($1); }
|
||||
| DT_KEY_IF DT_TOK_LPAR expression DT_TOK_RPAR statement_or_block {
|
||||
$$ = dt_node_if($3, $5, NULL);
|
||||
}
|
||||
| DT_KEY_IF DT_TOK_LPAR expression DT_TOK_RPAR
|
||||
statement_or_block DT_KEY_ELSE statement_or_block {
|
||||
$$ = dt_node_if($3, $5, $7);
|
||||
}
|
||||
;
|
||||
|
||||
argument_expression_list:
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DT_IMPL_H
|
||||
@ -320,6 +320,7 @@ struct dtrace_hdl {
|
||||
int dt_indent; /* recommended flow indent */
|
||||
dtrace_epid_t dt_last_epid; /* most recently consumed EPID */
|
||||
uint64_t dt_last_timestamp; /* most recently consumed timestamp */
|
||||
boolean_t dt_has_sugar; /* syntactic sugar used? */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -22,7 +22,7 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -115,8 +115,9 @@
|
||||
#define DT_VERS_1_11 DT_VERSION_NUMBER(1, 11, 0)
|
||||
#define DT_VERS_1_12 DT_VERSION_NUMBER(1, 12, 0)
|
||||
#define DT_VERS_1_12_1 DT_VERSION_NUMBER(1, 12, 1)
|
||||
#define DT_VERS_LATEST DT_VERS_1_12_1
|
||||
#define DT_VERS_STRING "Sun D 1.12.1"
|
||||
#define DT_VERS_1_13 DT_VERSION_NUMBER(1, 13, 0)
|
||||
#define DT_VERS_LATEST DT_VERS_1_13
|
||||
#define DT_VERS_STRING "Sun D 1.13"
|
||||
|
||||
const dt_version_t _dtrace_versions[] = {
|
||||
DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */
|
||||
@ -142,6 +143,7 @@ const dt_version_t _dtrace_versions[] = {
|
||||
DT_VERS_1_11, /* D API 1.11 */
|
||||
DT_VERS_1_12, /* D API 1.12 */
|
||||
DT_VERS_1_12_1, /* D API 1.12.1 */
|
||||
DT_VERS_1_13, /* D API 1.13 */
|
||||
0
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent Inc. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -2136,6 +2136,17 @@ dt_node_statement(dt_node_t *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)
|
||||
{
|
||||
@ -2205,7 +2216,6 @@ dt_node_clause(dt_node_t *pdescs, dt_node_t *pred, dt_node_t *acts)
|
||||
dnp->dn_pred = pred;
|
||||
dnp->dn_acts = acts;
|
||||
|
||||
yybegin(YYS_CLAUSE);
|
||||
return (dnp);
|
||||
}
|
||||
|
||||
@ -3197,8 +3207,9 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
|
||||
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:
|
||||
@ -3212,10 +3223,12 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
|
||||
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) {
|
||||
@ -3639,45 +3652,34 @@ asgn_common:
|
||||
|
||||
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 (strcmp(lp->dn_string, "self") == 0) {
|
||||
dhp = dtp->dt_tls;
|
||||
} else if (strcmp(lp->dn_string, "this") == 0) {
|
||||
dhp = yypcb->pcb_locals;
|
||||
}
|
||||
if (dhp != NULL) {
|
||||
if (rp->dn_kind != DT_NODE_VAR) {
|
||||
dt_xcook_ident(rp, dhp,
|
||||
DT_IDENT_SCALAR, B_TRUE);
|
||||
}
|
||||
|
||||
if (idflags != 0)
|
||||
rp = dt_node_cook(rp, idflags);
|
||||
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 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 (rp->dn_kind != DT_NODE_VAR) {
|
||||
dt_xcook_ident(rp, yypcb->pcb_locals,
|
||||
DT_IDENT_SCALAR, B_TRUE);
|
||||
/* avoid freeing rp */
|
||||
dnp->dn_right = dnp->dn_left;
|
||||
dt_node_free(dnp);
|
||||
return (rp);
|
||||
}
|
||||
|
||||
if (idflags != 0)
|
||||
rp = dt_node_cook(rp, idflags);
|
||||
|
||||
dnp->dn_right = dnp->dn_left; /* avoid freeing rp */
|
||||
dt_node_free(dnp);
|
||||
return (rp);
|
||||
}
|
||||
|
||||
/*FALLTHRU*/
|
||||
|
||||
case DT_TOK_DOT:
|
||||
lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF);
|
||||
|
||||
@ -4496,7 +4498,8 @@ static dt_node_t *(*dt_cook_funcs[])(dt_node_t *, uint_t) = {
|
||||
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 */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -4511,6 +4514,8 @@ dt_node_cook(dt_node_t *dnp, uint_t idflags)
|
||||
|
||||
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;
|
||||
|
||||
@ -4613,6 +4618,181 @@ dt_node_diftype(dtrace_hdl_t *dtp, const dt_node_t *dnp, dtrace_diftype_t *tp)
|
||||
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)
|
||||
{
|
||||
@ -4723,6 +4903,13 @@ dt_node_printr(dt_node_t *dnp, FILE *fp, int depth)
|
||||
(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:
|
||||
@ -4784,6 +4971,7 @@ dt_node_printr(dt_node_t *dnp, FILE *fp, int depth)
|
||||
|
||||
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:
|
||||
@ -4834,6 +5022,24 @@ dt_node_printr(dt_node_t *dnp, FILE *fp, int depth)
|
||||
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);
|
||||
|
@ -23,7 +23,7 @@
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013, 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
@ -105,6 +105,12 @@ typedef struct dt_node {
|
||||
struct dt_node *_probes; /* list of probe nodes */
|
||||
int _redecl; /* provider redeclared */
|
||||
} _provider;
|
||||
|
||||
struct {
|
||||
struct dt_node *_conditional;
|
||||
struct dt_node *_body;
|
||||
struct dt_node *_alternate_body;
|
||||
} _conditional;
|
||||
} dn_u;
|
||||
|
||||
struct dt_node *dn_list; /* parse tree list link */
|
||||
@ -140,6 +146,11 @@ typedef struct dt_node {
|
||||
#define dn_provred dn_u._provider._redecl /* DT_NODE_PROVIDER */
|
||||
#define dn_probes dn_u._provider._probes /* DT_NODE_PROVIDER */
|
||||
|
||||
/* DT_NODE_IF: */
|
||||
#define dn_conditional dn_u._conditional._conditional
|
||||
#define dn_body dn_u._conditional._body
|
||||
#define dn_alternate_body dn_u._conditional._alternate_body
|
||||
|
||||
#define DT_NODE_FREE 0 /* unused node (waiting to be freed) */
|
||||
#define DT_NODE_INT 1 /* integer value */
|
||||
#define DT_NODE_STRING 2 /* string value */
|
||||
@ -162,6 +173,7 @@ typedef struct dt_node {
|
||||
#define DT_NODE_PROBE 19 /* probe definition */
|
||||
#define DT_NODE_PROVIDER 20 /* provider definition */
|
||||
#define DT_NODE_PROG 21 /* program translation unit */
|
||||
#define DT_NODE_IF 22 /* if statement */
|
||||
|
||||
#define DT_NF_SIGNED 0x01 /* data is a signed quantity (else unsigned) */
|
||||
#define DT_NF_COOKED 0x02 /* data is a known type (else still cooking) */
|
||||
@ -213,6 +225,7 @@ extern dt_node_t *dt_node_xlator(dt_decl_t *, dt_decl_t *, char *, dt_node_t *);
|
||||
extern dt_node_t *dt_node_probe(char *, int, dt_node_t *, dt_node_t *);
|
||||
extern dt_node_t *dt_node_provider(char *, dt_node_t *);
|
||||
extern dt_node_t *dt_node_program(dt_node_t *);
|
||||
extern dt_node_t *dt_node_if(dt_node_t *, dt_node_t *, dt_node_t *);
|
||||
|
||||
extern dt_node_t *dt_node_link(dt_node_t *, dt_node_t *);
|
||||
extern dt_node_t *dt_node_cook(dt_node_t *, uint_t);
|
||||
@ -237,6 +250,7 @@ extern void dt_node_promote(dt_node_t *, dt_node_t *, dt_node_t *);
|
||||
extern void dt_node_diftype(dtrace_hdl_t *,
|
||||
const dt_node_t *, dtrace_diftype_t *);
|
||||
extern void dt_node_printr(dt_node_t *, FILE *, int);
|
||||
extern void dt_printd(dt_node_t *, FILE *, int);
|
||||
extern const char *dt_node_name(const dt_node_t *, char *, size_t);
|
||||
extern int dt_node_root(dt_node_t *);
|
||||
|
||||
|
516
lib/libdtrace/common/dt_sugar.c
Normal file
516
lib/libdtrace/common/dt_sugar.c
Normal file
@ -0,0 +1,516 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Syntactic sugar features are implemented by transforming the D parse tree
|
||||
* such that it only uses the subset of D that is supported by the rest of the
|
||||
* compiler / the kernel. A clause containing these language features is
|
||||
* referred to as a "super-clause", and its transformation typically entails
|
||||
* creating several "sub-clauses" to implement it. For diagnosability, the
|
||||
* sub-clauses will be printed if the "-xtree=8" flag is specified.
|
||||
*
|
||||
* Currently, the only syntactic sugar feature is "if/else" statements. Each
|
||||
* basic block (e.g. the body of the "if" and "else" statements, and the
|
||||
* statements before and after) is turned into its own sub-clause, with a
|
||||
* predicate that causes it to be executed only if the code flows to this point.
|
||||
* Nested if/else statements are supported.
|
||||
*
|
||||
* This infrastructure is designed to accommodate other syntactic sugar features
|
||||
* in the future.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <dt_module.h>
|
||||
#include <dt_program.h>
|
||||
#include <dt_provider.h>
|
||||
#include <dt_printf.h>
|
||||
#include <dt_pid.h>
|
||||
#include <dt_grammar.h>
|
||||
#include <dt_ident.h>
|
||||
#include <dt_string.h>
|
||||
#include <dt_impl.h>
|
||||
|
||||
typedef struct dt_sugar_parse {
|
||||
dtrace_hdl_t *dtsp_dtp; /* dtrace handle */
|
||||
dt_node_t *dtsp_pdescs; /* probe descriptions */
|
||||
int dtsp_num_conditions; /* number of condition variables */
|
||||
int dtsp_num_ifs; /* number of "if" statements */
|
||||
dt_node_t *dtsp_clause_list; /* list of clauses */
|
||||
} dt_sugar_parse_t;
|
||||
|
||||
static void dt_sugar_visit_stmts(dt_sugar_parse_t *, dt_node_t *, int);
|
||||
|
||||
/*
|
||||
* Return a node for "self->%error".
|
||||
*
|
||||
* Note that the "%" is part of the variable name, and is included so that
|
||||
* this variable name can not collide with any user-specified variable.
|
||||
*
|
||||
* This error variable is used to keep track of if there has been an error
|
||||
* in any of the sub-clauses, and is used to prevent execution of subsequent
|
||||
* sub-clauses following an error.
|
||||
*/
|
||||
static dt_node_t *
|
||||
dt_sugar_new_error_var(void)
|
||||
{
|
||||
return (dt_node_op2(DT_TOK_PTR, dt_node_ident(strdup("self")),
|
||||
dt_node_ident(strdup("%error"))));
|
||||
}
|
||||
|
||||
/*
|
||||
* Append this clause to the clause list.
|
||||
*/
|
||||
static void
|
||||
dt_sugar_append_clause(dt_sugar_parse_t *dp, dt_node_t *clause)
|
||||
{
|
||||
dp->dtsp_clause_list = dt_node_link(dp->dtsp_clause_list, clause);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepend this clause to the clause list.
|
||||
*/
|
||||
static void
|
||||
dt_sugar_prepend_clause(dt_sugar_parse_t *dp, dt_node_t *clause)
|
||||
{
|
||||
dp->dtsp_clause_list = dt_node_link(clause, dp->dtsp_clause_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a node for "this->%condition_<condid>", or NULL if condid==0.
|
||||
*
|
||||
* Note that the "%" is part of the variable name, and is included so that
|
||||
* this variable name can not collide with any user-specified variable.
|
||||
*/
|
||||
static dt_node_t *
|
||||
dt_sugar_new_condition_var(int condid)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if (condid == 0)
|
||||
return (NULL);
|
||||
assert(condid > 0);
|
||||
|
||||
(void) asprintf(&str, "%%condition_%d", ABS(condid));
|
||||
return (dt_node_op2(DT_TOK_PTR, dt_node_ident(strdup("this")),
|
||||
dt_node_ident(str)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return new clause to evaluate predicate and set newcond. condid is
|
||||
* the condition that we are already under, or 0 if none.
|
||||
* The new clause will be of the form:
|
||||
*
|
||||
* dp_pdescs
|
||||
* /!self->%error/
|
||||
* {
|
||||
* this->%condition_<newcond> =
|
||||
* (this->%condition_<condid> && pred);
|
||||
* }
|
||||
*
|
||||
* Note: if condid==0, we will instead do "... = (1 && pred)", to effectively
|
||||
* convert the pred to a boolean.
|
||||
*
|
||||
* Note: Unless an error has been encountered, we always set the condition
|
||||
* variable (either to 0 or 1). This lets us avoid resetting the condition
|
||||
* variables back to 0 when the super-clause completes.
|
||||
*/
|
||||
static dt_node_t *
|
||||
dt_sugar_new_condition_impl(dt_sugar_parse_t *dp,
|
||||
dt_node_t *pred, int condid, int newcond)
|
||||
{
|
||||
dt_node_t *value, *body, *newpred;
|
||||
|
||||
/* predicate is !self->%error */
|
||||
newpred = dt_node_op1(DT_TOK_LNEG, dt_sugar_new_error_var());
|
||||
|
||||
if (condid == 0) {
|
||||
/*
|
||||
* value is (1 && pred)
|
||||
*
|
||||
* Note, D doesn't allow a probe-local "this" variable to
|
||||
* be reused as a different type, even from a different probe.
|
||||
* Therefore, value can't simply be <pred>, because then
|
||||
* its type could be different when we reuse this condid
|
||||
* in a different meta-clause.
|
||||
*/
|
||||
value = dt_node_op2(DT_TOK_LAND, dt_node_int(1), pred);
|
||||
} else {
|
||||
/* value is (this->%condition_<condid> && pred) */
|
||||
value = dt_node_op2(DT_TOK_LAND,
|
||||
dt_sugar_new_condition_var(condid), pred);
|
||||
}
|
||||
|
||||
/* body is "this->%condition_<retval> = <value>;" */
|
||||
body = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
|
||||
dt_sugar_new_condition_var(newcond), value));
|
||||
|
||||
return (dt_node_clause(dp->dtsp_pdescs, newpred, body));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a new clause to evaluate predicate and set a new condition variable,
|
||||
* whose ID will be returned. The new clause will be appended to
|
||||
* dp_first_new_clause.
|
||||
*/
|
||||
static int
|
||||
dt_sugar_new_condition(dt_sugar_parse_t *dp, dt_node_t *pred, int condid)
|
||||
{
|
||||
dp->dtsp_num_conditions++;
|
||||
dt_sugar_append_clause(dp, dt_sugar_new_condition_impl(dp,
|
||||
pred, condid, dp->dtsp_num_conditions));
|
||||
return (dp->dtsp_num_conditions);
|
||||
}
|
||||
|
||||
/*
|
||||
* Visit the specified node and all of its descendants. Currently this is only
|
||||
* used to count the number of "if" statements (dtsp_num_ifs).
|
||||
*/
|
||||
static void
|
||||
dt_sugar_visit_all(dt_sugar_parse_t *dp, dt_node_t *dnp)
|
||||
{
|
||||
dt_node_t *arg;
|
||||
|
||||
switch (dnp->dn_kind) {
|
||||
case DT_NODE_FREE:
|
||||
case DT_NODE_INT:
|
||||
case DT_NODE_STRING:
|
||||
case DT_NODE_SYM:
|
||||
case DT_NODE_TYPE:
|
||||
case DT_NODE_PROBE:
|
||||
case DT_NODE_PDESC:
|
||||
case DT_NODE_IDENT:
|
||||
break;
|
||||
|
||||
case DT_NODE_FUNC:
|
||||
for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
break;
|
||||
|
||||
case DT_NODE_OP1:
|
||||
dt_sugar_visit_all(dp, dnp->dn_child);
|
||||
break;
|
||||
|
||||
case DT_NODE_OP2:
|
||||
dt_sugar_visit_all(dp, dnp->dn_left);
|
||||
dt_sugar_visit_all(dp, dnp->dn_right);
|
||||
if (dnp->dn_op == DT_TOK_LBRAC) {
|
||||
dt_node_t *ln = dnp->dn_right;
|
||||
while (ln->dn_list != NULL) {
|
||||
dt_sugar_visit_all(dp, ln->dn_list);
|
||||
ln = ln->dn_list;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DT_NODE_OP3:
|
||||
dt_sugar_visit_all(dp, dnp->dn_expr);
|
||||
dt_sugar_visit_all(dp, dnp->dn_left);
|
||||
dt_sugar_visit_all(dp, dnp->dn_right);
|
||||
break;
|
||||
|
||||
case DT_NODE_DEXPR:
|
||||
case DT_NODE_DFUNC:
|
||||
dt_sugar_visit_all(dp, dnp->dn_expr);
|
||||
break;
|
||||
|
||||
case DT_NODE_AGG:
|
||||
for (arg = dnp->dn_aggtup; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
|
||||
if (dnp->dn_aggfun)
|
||||
dt_sugar_visit_all(dp, dnp->dn_aggfun);
|
||||
break;
|
||||
|
||||
case DT_NODE_CLAUSE:
|
||||
for (arg = dnp->dn_pdescs; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
|
||||
if (dnp->dn_pred != NULL)
|
||||
dt_sugar_visit_all(dp, dnp->dn_pred);
|
||||
|
||||
for (arg = dnp->dn_acts; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
break;
|
||||
|
||||
case DT_NODE_INLINE: {
|
||||
const dt_idnode_t *inp = dnp->dn_ident->di_iarg;
|
||||
|
||||
dt_sugar_visit_all(dp, inp->din_root);
|
||||
break;
|
||||
}
|
||||
case DT_NODE_MEMBER:
|
||||
if (dnp->dn_membexpr)
|
||||
dt_sugar_visit_all(dp, dnp->dn_membexpr);
|
||||
break;
|
||||
|
||||
case DT_NODE_XLATOR:
|
||||
for (arg = dnp->dn_members; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
break;
|
||||
|
||||
case DT_NODE_PROVIDER:
|
||||
for (arg = dnp->dn_probes; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
break;
|
||||
|
||||
case DT_NODE_PROG:
|
||||
for (arg = dnp->dn_list; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
break;
|
||||
|
||||
case DT_NODE_IF:
|
||||
dp->dtsp_num_ifs++;
|
||||
dt_sugar_visit_all(dp, dnp->dn_conditional);
|
||||
|
||||
for (arg = dnp->dn_body; arg != NULL; arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
for (arg = dnp->dn_alternate_body; arg != NULL;
|
||||
arg = arg->dn_list)
|
||||
dt_sugar_visit_all(dp, arg);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
(void) dnerror(dnp, D_UNKNOWN, "bad node %p, kind %d\n",
|
||||
(void *)dnp, dnp->dn_kind);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a new clause which resets the error variable to zero:
|
||||
*
|
||||
* dp_pdescs{ self->%error = 0; }
|
||||
*
|
||||
* This clause will be executed at the beginning of each meta-clause, to
|
||||
* ensure the error variable is unset (in case the previous meta-clause
|
||||
* failed).
|
||||
*/
|
||||
static dt_node_t *
|
||||
dt_sugar_new_clearerror_clause(dt_sugar_parse_t *dp)
|
||||
{
|
||||
dt_node_t *stmt = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
|
||||
dt_sugar_new_error_var(), dt_node_int(0)));
|
||||
return (dt_node_clause(dp->dtsp_pdescs, NULL, stmt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate the conditional, and recursively visit the body of the "if"
|
||||
* statement (and the "else", if present).
|
||||
*/
|
||||
static void
|
||||
dt_sugar_do_if(dt_sugar_parse_t *dp, dt_node_t *if_stmt, int precondition)
|
||||
{
|
||||
int newid;
|
||||
|
||||
assert(if_stmt->dn_kind == DT_NODE_IF);
|
||||
|
||||
/* condition */
|
||||
newid = dt_sugar_new_condition(dp,
|
||||
if_stmt->dn_conditional, precondition);
|
||||
|
||||
/* body of if */
|
||||
dt_sugar_visit_stmts(dp, if_stmt->dn_body, newid);
|
||||
|
||||
/*
|
||||
* Visit the body of the "else" statement, if present. Note that we
|
||||
* generate a new condition which is the inverse of the previous
|
||||
* condition.
|
||||
*/
|
||||
if (if_stmt->dn_alternate_body != NULL) {
|
||||
dt_node_t *pred =
|
||||
dt_node_op1(DT_TOK_LNEG, dt_sugar_new_condition_var(newid));
|
||||
dt_sugar_visit_stmts(dp, if_stmt->dn_alternate_body,
|
||||
dt_sugar_new_condition(dp, pred, precondition));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a new clause to evaluate the statements based on the condition.
|
||||
* The new clause will be appended to dp_first_new_clause.
|
||||
*
|
||||
* dp_pdescs
|
||||
* /!self->%error && this->%condition_<condid>/
|
||||
* {
|
||||
* stmts
|
||||
* }
|
||||
*/
|
||||
static void
|
||||
dt_sugar_new_basic_block(dt_sugar_parse_t *dp, int condid, dt_node_t *stmts)
|
||||
{
|
||||
dt_node_t *pred = NULL;
|
||||
|
||||
if (condid == 0) {
|
||||
/*
|
||||
* Don't bother with !error on the first clause, because if
|
||||
* there is only one clause, we don't add the prelude to
|
||||
* zero out %error.
|
||||
*/
|
||||
if (dp->dtsp_num_conditions != 0) {
|
||||
pred = dt_node_op1(DT_TOK_LNEG,
|
||||
dt_sugar_new_error_var());
|
||||
}
|
||||
} else {
|
||||
pred = dt_node_op2(DT_TOK_LAND,
|
||||
dt_node_op1(DT_TOK_LNEG, dt_sugar_new_error_var()),
|
||||
dt_sugar_new_condition_var(condid));
|
||||
}
|
||||
dt_sugar_append_clause(dp,
|
||||
dt_node_clause(dp->dtsp_pdescs, pred, stmts));
|
||||
}
|
||||
|
||||
/*
|
||||
* Visit all the statements in this list, and break them into basic blocks,
|
||||
* generating new clauses for "if" and "else" statements.
|
||||
*/
|
||||
static void
|
||||
dt_sugar_visit_stmts(dt_sugar_parse_t *dp, dt_node_t *stmts, int precondition)
|
||||
{
|
||||
dt_node_t *stmt;
|
||||
dt_node_t *prev_stmt = NULL;
|
||||
dt_node_t *next_stmt;
|
||||
dt_node_t *first_stmt_in_basic_block = NULL;
|
||||
|
||||
for (stmt = stmts; stmt != NULL; stmt = next_stmt) {
|
||||
next_stmt = stmt->dn_list;
|
||||
|
||||
if (stmt->dn_kind != DT_NODE_IF) {
|
||||
if (first_stmt_in_basic_block == NULL)
|
||||
first_stmt_in_basic_block = stmt;
|
||||
prev_stmt = stmt;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove this and following statements from the previous
|
||||
* clause.
|
||||
*/
|
||||
if (prev_stmt != NULL)
|
||||
prev_stmt->dn_list = NULL;
|
||||
|
||||
/*
|
||||
* Generate clause for statements preceding the "if"
|
||||
*/
|
||||
if (first_stmt_in_basic_block != NULL) {
|
||||
dt_sugar_new_basic_block(dp, precondition,
|
||||
first_stmt_in_basic_block);
|
||||
}
|
||||
|
||||
dt_sugar_do_if(dp, stmt, precondition);
|
||||
|
||||
first_stmt_in_basic_block = NULL;
|
||||
|
||||
prev_stmt = stmt;
|
||||
}
|
||||
|
||||
/* generate clause for statements after last "if". */
|
||||
if (first_stmt_in_basic_block != NULL) {
|
||||
dt_sugar_new_basic_block(dp, precondition,
|
||||
first_stmt_in_basic_block);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a new clause which will set the error variable when an error occurs.
|
||||
* Only one of these clauses is created per program (e.g. script file).
|
||||
* The clause is:
|
||||
*
|
||||
* dtrace:::ERROR{ self->%error = 1; }
|
||||
*/
|
||||
static dt_node_t *
|
||||
dt_sugar_makeerrorclause(void)
|
||||
{
|
||||
dt_node_t *acts, *pdesc;
|
||||
|
||||
pdesc = dt_node_pdesc_by_name(strdup("dtrace:::ERROR"));
|
||||
|
||||
acts = dt_node_statement(dt_node_op2(DT_TOK_ASGN,
|
||||
dt_sugar_new_error_var(), dt_node_int(1)));
|
||||
|
||||
return (dt_node_clause(pdesc, NULL, acts));
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform the super-clause into straight-D, returning the new list of
|
||||
* sub-clauses.
|
||||
*/
|
||||
dt_node_t *
|
||||
dt_compile_sugar(dtrace_hdl_t *dtp, dt_node_t *clause)
|
||||
{
|
||||
dt_sugar_parse_t dp = { 0 };
|
||||
int condid = 0;
|
||||
|
||||
dp.dtsp_dtp = dtp;
|
||||
dp.dtsp_pdescs = clause->dn_pdescs;
|
||||
|
||||
/* make dt_node_int() generate an "int"-typed integer */
|
||||
yyintdecimal = B_TRUE;
|
||||
yyintsuffix[0] = '\0';
|
||||
yyintprefix = 0;
|
||||
|
||||
dt_sugar_visit_all(&dp, clause);
|
||||
|
||||
if (dp.dtsp_num_ifs == 0 && dp.dtsp_num_conditions == 0) {
|
||||
/*
|
||||
* There is nothing that modifies the number of clauses. Use
|
||||
* the existing clause as-is, with its predicate intact. This
|
||||
* ensures that in the absence of D sugar, the body of the
|
||||
* clause can create a variable that is referenced in the
|
||||
* predicate.
|
||||
*/
|
||||
dt_sugar_append_clause(&dp, dt_node_clause(clause->dn_pdescs,
|
||||
clause->dn_pred, clause->dn_acts));
|
||||
} else {
|
||||
if (clause->dn_pred != NULL) {
|
||||
condid = dt_sugar_new_condition(&dp,
|
||||
clause->dn_pred, condid);
|
||||
}
|
||||
|
||||
if (clause->dn_acts == NULL) {
|
||||
/*
|
||||
* dt_sugar_visit_stmts() does not emit a clause with
|
||||
* an empty body (e.g. if there's an empty "if" body),
|
||||
* but we need the empty body here so that we
|
||||
* continue to get the default tracing action.
|
||||
*/
|
||||
dt_sugar_new_basic_block(&dp, condid, NULL);
|
||||
} else {
|
||||
dt_sugar_visit_stmts(&dp, clause->dn_acts, condid);
|
||||
}
|
||||
}
|
||||
|
||||
if (dp.dtsp_num_conditions != 0) {
|
||||
dt_sugar_prepend_clause(&dp,
|
||||
dt_sugar_new_clearerror_clause(&dp));
|
||||
}
|
||||
|
||||
if (dp.dtsp_clause_list != NULL &&
|
||||
dp.dtsp_clause_list->dn_list != NULL && !dtp->dt_has_sugar) {
|
||||
dtp->dt_has_sugar = B_TRUE;
|
||||
dt_sugar_prepend_clause(&dp, dt_sugar_makeerrorclause());
|
||||
}
|
||||
return (dp.dtsp_clause_list);
|
||||
}
|
@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
@ -55,6 +55,7 @@ extern "C" {
|
||||
#define DTRACE_VERSION 3 /* library ABI interface version */
|
||||
|
||||
struct ps_prochandle;
|
||||
struct dt_node;
|
||||
typedef struct dtrace_hdl dtrace_hdl_t;
|
||||
typedef struct dtrace_prog dtrace_prog_t;
|
||||
typedef struct dtrace_vector dtrace_vector_t;
|
||||
@ -111,7 +112,7 @@ typedef struct dtrace_proginfo {
|
||||
#define DTRACE_C_CPP 0x0010 /* Preprocess input file with cpp(1) utility */
|
||||
#define DTRACE_C_KNODEF 0x0020 /* Permit unresolved kernel symbols in DIFO */
|
||||
#define DTRACE_C_UNODEF 0x0040 /* Permit unresolved user symbols in DIFO */
|
||||
#define DTRACE_C_PSPEC 0x0080 /* Intepret ambiguous specifiers as probes */
|
||||
#define DTRACE_C_PSPEC 0x0080 /* Interpret ambiguous specifiers as probes */
|
||||
#define DTRACE_C_ETAGS 0x0100 /* Prefix error messages with error tags */
|
||||
#define DTRACE_C_ARGREF 0x0200 /* Do not require all macro args to be used */
|
||||
#define DTRACE_C_DEFARG 0x0800 /* Use 0/"" as value for unspecified args */
|
||||
@ -519,6 +520,10 @@ extern int dtrace_type_strcompile(dtrace_hdl_t *,
|
||||
extern int dtrace_type_fcompile(dtrace_hdl_t *,
|
||||
FILE *, dtrace_typeinfo_t *);
|
||||
|
||||
extern struct dt_node *dt_compile_sugar(dtrace_hdl_t *,
|
||||
struct dt_node *);
|
||||
|
||||
|
||||
/*
|
||||
* DTrace Probe Interface
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user