6767 C(TOO_MANY_SORT_FIELDS, "Too many sort fields (Max = 2)"), \
6868 C(INVALID_SORT_FIELD, "Sort field must be a key or a val"), \
6969 C(INVALID_STR_OPERAND, "String type can not be an operand in expression"), \
70- C(EXPECT_NUMBER, "Expecting numeric literal"),
70+ C(EXPECT_NUMBER, "Expecting numeric literal"), \
71+ C(UNARY_MINUS_SUBEXPR, "Unary minus not supported in sub-expressions"), \
72+ C(SYM_OFFSET_SUBEXPR, ".sym-offset not supported in sub-expressions"),
7173
7274#undef C
7375#define C (a , b ) HIST_ERR_##a
@@ -1634,40 +1636,96 @@ static char *expr_str(struct hist_field *field, unsigned int level)
16341636 return expr ;
16351637}
16361638
1637- static int contains_operator (char * str )
1639+ /*
1640+ * If field_op != FIELD_OP_NONE, *sep points to the root operator
1641+ * of the expression tree to be evaluated.
1642+ */
1643+ static int contains_operator (char * str , char * * sep )
16381644{
16391645 enum field_op_id field_op = FIELD_OP_NONE ;
1640- char * op ;
1646+ char * minus_op , * plus_op , * div_op , * mult_op ;
1647+
1648+
1649+ /*
1650+ * Report the last occurrence of the operators first, so that the
1651+ * expression is evaluated left to right. This is important since
1652+ * subtraction and division are not associative.
1653+ *
1654+ * e.g
1655+ * 64/8/4/2 is 1, i.e 64/8/4/2 = ((64/8)/4)/2
1656+ * 14-7-5-2 is 0, i.e 14-7-5-2 = ((14-7)-5)-2
1657+ */
16411658
1642- op = strpbrk (str , "+-/*" );
1643- if (!op )
1644- return FIELD_OP_NONE ;
1659+ /*
1660+ * First, find lower precedence addition and subtraction
1661+ * since the expression will be evaluated recursively.
1662+ */
1663+ minus_op = strrchr (str , '-' );
1664+ if (minus_op ) {
1665+ /* Unfortunately, the modifier ".sym-offset" can confuse things. */
1666+ if (minus_op - str >= 4 && !strncmp (minus_op - 4 , ".sym-offset" , 11 ))
1667+ goto out ;
16451668
1646- switch (* op ) {
1647- case '-' :
16481669 /*
1649- * Unfortunately, the modifier ".sym-offset"
1650- * can confuse things .
1670+ * Unary minus is not supported in sub-expressions. If
1671+ * present, it is always the next root operator .
16511672 */
1652- if (op - str >= 4 && !strncmp (op - 4 , ".sym-offset" , 11 ))
1653- return FIELD_OP_NONE ;
1654-
1655- if (* str == '-' )
1673+ if (minus_op == str ) {
16561674 field_op = FIELD_OP_UNARY_MINUS ;
1657- else
1658- field_op = FIELD_OP_MINUS ;
1659- break ;
1660- case '+' :
1661- field_op = FIELD_OP_PLUS ;
1662- break ;
1663- case '/' :
1675+ goto out ;
1676+ }
1677+
1678+ field_op = FIELD_OP_MINUS ;
1679+ }
1680+
1681+ plus_op = strrchr (str , '+' );
1682+ if (plus_op || minus_op ) {
1683+ /*
1684+ * For operators of the same precedence use to rightmost as the
1685+ * root, so that the expression is evaluated left to right.
1686+ */
1687+ if (plus_op > minus_op )
1688+ field_op = FIELD_OP_PLUS ;
1689+ goto out ;
1690+ }
1691+
1692+ /*
1693+ * Multiplication and division have higher precedence than addition and
1694+ * subtraction.
1695+ */
1696+ div_op = strrchr (str , '/' );
1697+ if (div_op )
16641698 field_op = FIELD_OP_DIV ;
1665- break ;
1666- case '*' :
1699+
1700+ mult_op = strrchr (str , '*' );
1701+ /*
1702+ * For operators of the same precedence use to rightmost as the
1703+ * root, so that the expression is evaluated left to right.
1704+ */
1705+ if (mult_op > div_op )
16671706 field_op = FIELD_OP_MULT ;
1668- break ;
1669- default :
1670- break ;
1707+
1708+ out :
1709+ if (sep ) {
1710+ switch (field_op ) {
1711+ case FIELD_OP_UNARY_MINUS :
1712+ case FIELD_OP_MINUS :
1713+ * sep = minus_op ;
1714+ break ;
1715+ case FIELD_OP_PLUS :
1716+ * sep = plus_op ;
1717+ break ;
1718+ case FIELD_OP_DIV :
1719+ * sep = div_op ;
1720+ break ;
1721+ case FIELD_OP_MULT :
1722+ * sep = mult_op ;
1723+ break ;
1724+ case FIELD_OP_NONE :
1725+ default :
1726+ * sep = NULL ;
1727+ break ;
1728+ }
16711729 }
16721730
16731731 return field_op ;
@@ -1997,7 +2055,7 @@ static char *field_name_from_var(struct hist_trigger_data *hist_data,
19972055
19982056 if (strcmp (var_name , name ) == 0 ) {
19992057 field = hist_data -> attrs -> var_defs .expr [i ];
2000- if (contains_operator (field ) || is_var_ref (field ))
2058+ if (contains_operator (field , NULL ) || is_var_ref (field ))
20012059 continue ;
20022060 return field ;
20032061 }
@@ -2260,21 +2318,24 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
22602318static struct hist_field * parse_expr (struct hist_trigger_data * hist_data ,
22612319 struct trace_event_file * file ,
22622320 char * str , unsigned long flags ,
2263- char * var_name , unsigned int level );
2321+ char * var_name , unsigned int * n_subexprs );
22642322
22652323static struct hist_field * parse_unary (struct hist_trigger_data * hist_data ,
22662324 struct trace_event_file * file ,
22672325 char * str , unsigned long flags ,
2268- char * var_name , unsigned int level )
2326+ char * var_name , unsigned int * n_subexprs )
22692327{
22702328 struct hist_field * operand1 , * expr = NULL ;
22712329 unsigned long operand_flags ;
22722330 int ret = 0 ;
22732331 char * s ;
22742332
2333+ /* Unary minus operator, increment n_subexprs */
2334+ ++ * n_subexprs ;
2335+
22752336 /* we support only -(xxx) i.e. explicit parens required */
22762337
2277- if (level > 3 ) {
2338+ if (* n_subexprs > 3 ) {
22782339 hist_err (file -> tr , HIST_ERR_TOO_MANY_SUBEXPR , errpos (str ));
22792340 ret = - EINVAL ;
22802341 goto free ;
@@ -2291,8 +2352,16 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
22912352 }
22922353
22932354 s = strrchr (str , ')' );
2294- if (s )
2355+ if (s ) {
2356+ /* unary minus not supported in sub-expressions */
2357+ if (* (s + 1 ) != '\0' ) {
2358+ hist_err (file -> tr , HIST_ERR_UNARY_MINUS_SUBEXPR ,
2359+ errpos (str ));
2360+ ret = - EINVAL ;
2361+ goto free ;
2362+ }
22952363 * s = '\0' ;
2364+ }
22962365 else {
22972366 ret = - EINVAL ; /* no closing ')' */
22982367 goto free ;
@@ -2306,7 +2375,7 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
23062375 }
23072376
23082377 operand_flags = 0 ;
2309- operand1 = parse_expr (hist_data , file , str , operand_flags , NULL , ++ level );
2378+ operand1 = parse_expr (hist_data , file , str , operand_flags , NULL , n_subexprs );
23102379 if (IS_ERR (operand1 )) {
23112380 ret = PTR_ERR (operand1 );
23122381 goto free ;
@@ -2376,60 +2445,61 @@ static int check_expr_operands(struct trace_array *tr,
23762445static struct hist_field * parse_expr (struct hist_trigger_data * hist_data ,
23772446 struct trace_event_file * file ,
23782447 char * str , unsigned long flags ,
2379- char * var_name , unsigned int level )
2448+ char * var_name , unsigned int * n_subexprs )
23802449{
23812450 struct hist_field * operand1 = NULL , * operand2 = NULL , * expr = NULL ;
23822451 unsigned long operand_flags ;
23832452 int field_op , ret = - EINVAL ;
23842453 char * sep , * operand1_str ;
23852454
2386- if (level > 3 ) {
2455+ if (* n_subexprs > 3 ) {
23872456 hist_err (file -> tr , HIST_ERR_TOO_MANY_SUBEXPR , errpos (str ));
23882457 return ERR_PTR (- EINVAL );
23892458 }
23902459
2391- field_op = contains_operator (str );
2460+ /*
2461+ * ".sym-offset" in expressions has no effect on their evaluation,
2462+ * but can confuse operator parsing.
2463+ */
2464+ if (* n_subexprs == 0 ) {
2465+ sep = strstr (str , ".sym-offset" );
2466+ if (sep ) {
2467+ * sep = '\0' ;
2468+ if (strpbrk (str , "+-/*" ) || strpbrk (sep + 11 , "+-/*" )) {
2469+ * sep = '.' ;
2470+ hist_err (file -> tr , HIST_ERR_SYM_OFFSET_SUBEXPR ,
2471+ errpos (sep ));
2472+ return ERR_PTR (- EINVAL );
2473+ }
2474+ * sep = '.' ;
2475+ }
2476+ }
2477+
2478+ field_op = contains_operator (str , & sep );
23922479
23932480 if (field_op == FIELD_OP_NONE )
23942481 return parse_atom (hist_data , file , str , & flags , var_name );
23952482
23962483 if (field_op == FIELD_OP_UNARY_MINUS )
2397- return parse_unary (hist_data , file , str , flags , var_name , ++ level );
2484+ return parse_unary (hist_data , file , str , flags , var_name , n_subexprs );
23982485
2399- switch (field_op ) {
2400- case FIELD_OP_MINUS :
2401- sep = "-" ;
2402- break ;
2403- case FIELD_OP_PLUS :
2404- sep = "+" ;
2405- break ;
2406- case FIELD_OP_DIV :
2407- sep = "/" ;
2408- break ;
2409- case FIELD_OP_MULT :
2410- sep = "*" ;
2411- break ;
2412- default :
2413- goto free ;
2414- }
2486+ /* Binary operator found, increment n_subexprs */
2487+ ++ * n_subexprs ;
24152488
2416- /*
2417- * Multiplication and division are only supported in single operator
2418- * expressions, since the expression is always evaluated from right
2419- * to left.
2420- */
2421- if ((field_op == FIELD_OP_DIV || field_op == FIELD_OP_MULT ) && level > 0 ) {
2422- hist_err (file -> tr , HIST_ERR_TOO_MANY_SUBEXPR , errpos (str ));
2423- return ERR_PTR (- EINVAL );
2424- }
2489+ /* Split the expression string at the root operator */
2490+ if (!sep )
2491+ goto free ;
2492+ * sep = '\0' ;
2493+ operand1_str = str ;
2494+ str = sep + 1 ;
24252495
2426- operand1_str = strsep (& str , sep );
24272496 if (!operand1_str || !str )
24282497 goto free ;
24292498
24302499 operand_flags = 0 ;
2431- operand1 = parse_atom (hist_data , file , operand1_str ,
2432- & operand_flags , NULL );
2500+
2501+ /* LHS of string is an expression e.g. a+b in a+b+c */
2502+ operand1 = parse_expr (hist_data , file , operand1_str , operand_flags , NULL , n_subexprs );
24332503 if (IS_ERR (operand1 )) {
24342504 ret = PTR_ERR (operand1 );
24352505 operand1 = NULL ;
@@ -2441,9 +2511,9 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
24412511 goto free ;
24422512 }
24432513
2444- /* rest of string could be another expression e.g. b+ c in a+b+c */
2514+ /* RHS of string is another expression e.g. c in a+b+c */
24452515 operand_flags = 0 ;
2446- operand2 = parse_expr (hist_data , file , str , operand_flags , NULL , ++ level );
2516+ operand2 = parse_expr (hist_data , file , str , operand_flags , NULL , n_subexprs );
24472517 if (IS_ERR (operand2 )) {
24482518 ret = PTR_ERR (operand2 );
24492519 operand2 = NULL ;
@@ -3879,9 +3949,9 @@ static int __create_val_field(struct hist_trigger_data *hist_data,
38793949 unsigned long flags )
38803950{
38813951 struct hist_field * hist_field ;
3882- int ret = 0 ;
3952+ int ret = 0 , n_subexprs = 0 ;
38833953
3884- hist_field = parse_expr (hist_data , file , field_str , flags , var_name , 0 );
3954+ hist_field = parse_expr (hist_data , file , field_str , flags , var_name , & n_subexprs );
38853955 if (IS_ERR (hist_field )) {
38863956 ret = PTR_ERR (hist_field );
38873957 goto out ;
@@ -3984,7 +4054,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
39844054 struct hist_field * hist_field = NULL ;
39854055 unsigned long flags = 0 ;
39864056 unsigned int key_size ;
3987- int ret = 0 ;
4057+ int ret = 0 , n_subexprs = 0 ;
39884058
39894059 if (WARN_ON (key_idx >= HIST_FIELDS_MAX ))
39904060 return - EINVAL ;
@@ -3997,7 +4067,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
39974067 hist_field = create_hist_field (hist_data , NULL , flags , NULL );
39984068 } else {
39994069 hist_field = parse_expr (hist_data , file , field_str , flags ,
4000- NULL , 0 );
4070+ NULL , & n_subexprs );
40014071 if (IS_ERR (hist_field )) {
40024072 ret = PTR_ERR (hist_field );
40034073 goto out ;
0 commit comments