67
67
C(TOO_MANY_SORT_FIELDS, "Too many sort fields (Max = 2)"), \
68
68
C(INVALID_SORT_FIELD, "Sort field must be a key or a val"), \
69
69
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"),
71
73
72
74
#undef C
73
75
#define C (a , b ) HIST_ERR_##a
@@ -1644,40 +1646,96 @@ static char *expr_str(struct hist_field *field, unsigned int level)
1644
1646
return expr ;
1645
1647
}
1646
1648
1647
- static int contains_operator (char * str )
1649
+ /*
1650
+ * If field_op != FIELD_OP_NONE, *sep points to the root operator
1651
+ * of the expression tree to be evaluated.
1652
+ */
1653
+ static int contains_operator (char * str , char * * sep )
1648
1654
{
1649
1655
enum field_op_id field_op = FIELD_OP_NONE ;
1650
- char * op ;
1656
+ char * minus_op , * plus_op , * div_op , * mult_op ;
1657
+
1658
+
1659
+ /*
1660
+ * Report the last occurrence of the operators first, so that the
1661
+ * expression is evaluated left to right. This is important since
1662
+ * subtraction and division are not associative.
1663
+ *
1664
+ * e.g
1665
+ * 64/8/4/2 is 1, i.e 64/8/4/2 = ((64/8)/4)/2
1666
+ * 14-7-5-2 is 0, i.e 14-7-5-2 = ((14-7)-5)-2
1667
+ */
1651
1668
1652
- op = strpbrk (str , "+-/*" );
1653
- if (!op )
1654
- return FIELD_OP_NONE ;
1669
+ /*
1670
+ * First, find lower precedence addition and subtraction
1671
+ * since the expression will be evaluated recursively.
1672
+ */
1673
+ minus_op = strrchr (str , '-' );
1674
+ if (minus_op ) {
1675
+ /* Unfortunately, the modifier ".sym-offset" can confuse things. */
1676
+ if (minus_op - str >= 4 && !strncmp (minus_op - 4 , ".sym-offset" , 11 ))
1677
+ goto out ;
1655
1678
1656
- switch (* op ) {
1657
- case '-' :
1658
1679
/*
1659
- * Unfortunately, the modifier ".sym-offset"
1660
- * can confuse things .
1680
+ * Unary minus is not supported in sub-expressions. If
1681
+ * present, it is always the next root operator .
1661
1682
*/
1662
- if (op - str >= 4 && !strncmp (op - 4 , ".sym-offset" , 11 ))
1663
- return FIELD_OP_NONE ;
1664
-
1665
- if (* str == '-' )
1683
+ if (minus_op == str ) {
1666
1684
field_op = FIELD_OP_UNARY_MINUS ;
1667
- else
1668
- field_op = FIELD_OP_MINUS ;
1669
- break ;
1670
- case '+' :
1671
- field_op = FIELD_OP_PLUS ;
1672
- break ;
1673
- case '/' :
1685
+ goto out ;
1686
+ }
1687
+
1688
+ field_op = FIELD_OP_MINUS ;
1689
+ }
1690
+
1691
+ plus_op = strrchr (str , '+' );
1692
+ if (plus_op || minus_op ) {
1693
+ /*
1694
+ * For operators of the same precedence use to rightmost as the
1695
+ * root, so that the expression is evaluated left to right.
1696
+ */
1697
+ if (plus_op > minus_op )
1698
+ field_op = FIELD_OP_PLUS ;
1699
+ goto out ;
1700
+ }
1701
+
1702
+ /*
1703
+ * Multiplication and division have higher precedence than addition and
1704
+ * subtraction.
1705
+ */
1706
+ div_op = strrchr (str , '/' );
1707
+ if (div_op )
1674
1708
field_op = FIELD_OP_DIV ;
1675
- break ;
1676
- case '*' :
1709
+
1710
+ mult_op = strrchr (str , '*' );
1711
+ /*
1712
+ * For operators of the same precedence use to rightmost as the
1713
+ * root, so that the expression is evaluated left to right.
1714
+ */
1715
+ if (mult_op > div_op )
1677
1716
field_op = FIELD_OP_MULT ;
1678
- break ;
1679
- default :
1680
- break ;
1717
+
1718
+ out :
1719
+ if (sep ) {
1720
+ switch (field_op ) {
1721
+ case FIELD_OP_UNARY_MINUS :
1722
+ case FIELD_OP_MINUS :
1723
+ * sep = minus_op ;
1724
+ break ;
1725
+ case FIELD_OP_PLUS :
1726
+ * sep = plus_op ;
1727
+ break ;
1728
+ case FIELD_OP_DIV :
1729
+ * sep = div_op ;
1730
+ break ;
1731
+ case FIELD_OP_MULT :
1732
+ * sep = mult_op ;
1733
+ break ;
1734
+ case FIELD_OP_NONE :
1735
+ default :
1736
+ * sep = NULL ;
1737
+ break ;
1738
+ }
1681
1739
}
1682
1740
1683
1741
return field_op ;
@@ -2003,7 +2061,7 @@ static char *field_name_from_var(struct hist_trigger_data *hist_data,
2003
2061
2004
2062
if (strcmp (var_name , name ) == 0 ) {
2005
2063
field = hist_data -> attrs -> var_defs .expr [i ];
2006
- if (contains_operator (field ) || is_var_ref (field ))
2064
+ if (contains_operator (field , NULL ) || is_var_ref (field ))
2007
2065
continue ;
2008
2066
return field ;
2009
2067
}
@@ -2266,21 +2324,24 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
2266
2324
static struct hist_field * parse_expr (struct hist_trigger_data * hist_data ,
2267
2325
struct trace_event_file * file ,
2268
2326
char * str , unsigned long flags ,
2269
- char * var_name , unsigned int level );
2327
+ char * var_name , unsigned int * n_subexprs );
2270
2328
2271
2329
static struct hist_field * parse_unary (struct hist_trigger_data * hist_data ,
2272
2330
struct trace_event_file * file ,
2273
2331
char * str , unsigned long flags ,
2274
- char * var_name , unsigned int level )
2332
+ char * var_name , unsigned int * n_subexprs )
2275
2333
{
2276
2334
struct hist_field * operand1 , * expr = NULL ;
2277
2335
unsigned long operand_flags ;
2278
2336
int ret = 0 ;
2279
2337
char * s ;
2280
2338
2339
+ /* Unary minus operator, increment n_subexprs */
2340
+ ++ * n_subexprs ;
2341
+
2281
2342
/* we support only -(xxx) i.e. explicit parens required */
2282
2343
2283
- if (level > 3 ) {
2344
+ if (* n_subexprs > 3 ) {
2284
2345
hist_err (file -> tr , HIST_ERR_TOO_MANY_SUBEXPR , errpos (str ));
2285
2346
ret = - EINVAL ;
2286
2347
goto free ;
@@ -2297,8 +2358,16 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
2297
2358
}
2298
2359
2299
2360
s = strrchr (str , ')' );
2300
- if (s )
2361
+ if (s ) {
2362
+ /* unary minus not supported in sub-expressions */
2363
+ if (* (s + 1 ) != '\0' ) {
2364
+ hist_err (file -> tr , HIST_ERR_UNARY_MINUS_SUBEXPR ,
2365
+ errpos (str ));
2366
+ ret = - EINVAL ;
2367
+ goto free ;
2368
+ }
2301
2369
* s = '\0' ;
2370
+ }
2302
2371
else {
2303
2372
ret = - EINVAL ; /* no closing ')' */
2304
2373
goto free ;
@@ -2312,7 +2381,7 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
2312
2381
}
2313
2382
2314
2383
operand_flags = 0 ;
2315
- operand1 = parse_expr (hist_data , file , str , operand_flags , NULL , ++ level );
2384
+ operand1 = parse_expr (hist_data , file , str , operand_flags , NULL , n_subexprs );
2316
2385
if (IS_ERR (operand1 )) {
2317
2386
ret = PTR_ERR (operand1 );
2318
2387
goto free ;
@@ -2382,60 +2451,61 @@ static int check_expr_operands(struct trace_array *tr,
2382
2451
static struct hist_field * parse_expr (struct hist_trigger_data * hist_data ,
2383
2452
struct trace_event_file * file ,
2384
2453
char * str , unsigned long flags ,
2385
- char * var_name , unsigned int level )
2454
+ char * var_name , unsigned int * n_subexprs )
2386
2455
{
2387
2456
struct hist_field * operand1 = NULL , * operand2 = NULL , * expr = NULL ;
2388
2457
unsigned long operand_flags ;
2389
2458
int field_op , ret = - EINVAL ;
2390
2459
char * sep , * operand1_str ;
2391
2460
2392
- if (level > 3 ) {
2461
+ if (* n_subexprs > 3 ) {
2393
2462
hist_err (file -> tr , HIST_ERR_TOO_MANY_SUBEXPR , errpos (str ));
2394
2463
return ERR_PTR (- EINVAL );
2395
2464
}
2396
2465
2397
- field_op = contains_operator (str );
2466
+ /*
2467
+ * ".sym-offset" in expressions has no effect on their evaluation,
2468
+ * but can confuse operator parsing.
2469
+ */
2470
+ if (* n_subexprs == 0 ) {
2471
+ sep = strstr (str , ".sym-offset" );
2472
+ if (sep ) {
2473
+ * sep = '\0' ;
2474
+ if (strpbrk (str , "+-/*" ) || strpbrk (sep + 11 , "+-/*" )) {
2475
+ * sep = '.' ;
2476
+ hist_err (file -> tr , HIST_ERR_SYM_OFFSET_SUBEXPR ,
2477
+ errpos (sep ));
2478
+ return ERR_PTR (- EINVAL );
2479
+ }
2480
+ * sep = '.' ;
2481
+ }
2482
+ }
2483
+
2484
+ field_op = contains_operator (str , & sep );
2398
2485
2399
2486
if (field_op == FIELD_OP_NONE )
2400
2487
return parse_atom (hist_data , file , str , & flags , var_name );
2401
2488
2402
2489
if (field_op == FIELD_OP_UNARY_MINUS )
2403
- return parse_unary (hist_data , file , str , flags , var_name , ++ level );
2490
+ return parse_unary (hist_data , file , str , flags , var_name , n_subexprs );
2404
2491
2405
- switch (field_op ) {
2406
- case FIELD_OP_MINUS :
2407
- sep = "-" ;
2408
- break ;
2409
- case FIELD_OP_PLUS :
2410
- sep = "+" ;
2411
- break ;
2412
- case FIELD_OP_DIV :
2413
- sep = "/" ;
2414
- break ;
2415
- case FIELD_OP_MULT :
2416
- sep = "*" ;
2417
- break ;
2418
- default :
2419
- goto free ;
2420
- }
2492
+ /* Binary operator found, increment n_subexprs */
2493
+ ++ * n_subexprs ;
2421
2494
2422
- /*
2423
- * Multiplication and division are only supported in single operator
2424
- * expressions, since the expression is always evaluated from right
2425
- * to left.
2426
- */
2427
- if ((field_op == FIELD_OP_DIV || field_op == FIELD_OP_MULT ) && level > 0 ) {
2428
- hist_err (file -> tr , HIST_ERR_TOO_MANY_SUBEXPR , errpos (str ));
2429
- return ERR_PTR (- EINVAL );
2430
- }
2495
+ /* Split the expression string at the root operator */
2496
+ if (!sep )
2497
+ goto free ;
2498
+ * sep = '\0' ;
2499
+ operand1_str = str ;
2500
+ str = sep + 1 ;
2431
2501
2432
- operand1_str = strsep (& str , sep );
2433
2502
if (!operand1_str || !str )
2434
2503
goto free ;
2435
2504
2436
2505
operand_flags = 0 ;
2437
- operand1 = parse_atom (hist_data , file , operand1_str ,
2438
- & operand_flags , NULL );
2506
+
2507
+ /* LHS of string is an expression e.g. a+b in a+b+c */
2508
+ operand1 = parse_expr (hist_data , file , operand1_str , operand_flags , NULL , n_subexprs );
2439
2509
if (IS_ERR (operand1 )) {
2440
2510
ret = PTR_ERR (operand1 );
2441
2511
operand1 = NULL ;
@@ -2447,9 +2517,9 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
2447
2517
goto free ;
2448
2518
}
2449
2519
2450
- /* rest of string could be another expression e.g. b+ c in a+b+c */
2520
+ /* RHS of string is another expression e.g. c in a+b+c */
2451
2521
operand_flags = 0 ;
2452
- operand2 = parse_expr (hist_data , file , str , operand_flags , NULL , ++ level );
2522
+ operand2 = parse_expr (hist_data , file , str , operand_flags , NULL , n_subexprs );
2453
2523
if (IS_ERR (operand2 )) {
2454
2524
ret = PTR_ERR (operand2 );
2455
2525
operand2 = NULL ;
@@ -3883,9 +3953,9 @@ static int __create_val_field(struct hist_trigger_data *hist_data,
3883
3953
unsigned long flags )
3884
3954
{
3885
3955
struct hist_field * hist_field ;
3886
- int ret = 0 ;
3956
+ int ret = 0 , n_subexprs = 0 ;
3887
3957
3888
- hist_field = parse_expr (hist_data , file , field_str , flags , var_name , 0 );
3958
+ hist_field = parse_expr (hist_data , file , field_str , flags , var_name , & n_subexprs );
3889
3959
if (IS_ERR (hist_field )) {
3890
3960
ret = PTR_ERR (hist_field );
3891
3961
goto out ;
@@ -4026,7 +4096,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
4026
4096
struct hist_field * hist_field = NULL ;
4027
4097
unsigned long flags = 0 ;
4028
4098
unsigned int key_size ;
4029
- int ret = 0 ;
4099
+ int ret = 0 , n_subexprs = 0 ;
4030
4100
4031
4101
if (WARN_ON (key_idx >= HIST_FIELDS_MAX ))
4032
4102
return - EINVAL ;
@@ -4039,7 +4109,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
4039
4109
hist_field = create_hist_field (hist_data , NULL , flags , NULL );
4040
4110
} else {
4041
4111
hist_field = parse_expr (hist_data , file , field_str , flags ,
4042
- NULL , 0 );
4112
+ NULL , & n_subexprs );
4043
4113
if (IS_ERR (hist_field )) {
4044
4114
ret = PTR_ERR (hist_field );
4045
4115
goto out ;
0 commit comments