18
18
#include <libelf.h>
19
19
#include <gelf.h>
20
20
#include <float.h>
21
+ #include <math.h>
21
22
22
23
#ifndef ARRAY_SIZE
23
24
#define ARRAY_SIZE (arr ) (sizeof(arr) / sizeof((arr)[0]))
@@ -99,6 +100,7 @@ struct stat_specs {
99
100
enum stat_id ids [ALL_STATS_CNT ];
100
101
enum stat_variant variants [ALL_STATS_CNT ];
101
102
bool asc [ALL_STATS_CNT ];
103
+ bool abs [ALL_STATS_CNT ];
102
104
int lens [ALL_STATS_CNT * 3 ]; /* 3x for comparison mode */
103
105
};
104
106
@@ -133,6 +135,7 @@ struct filter {
133
135
int stat_id ;
134
136
enum stat_variant stat_var ;
135
137
long value ;
138
+ bool abs ;
136
139
};
137
140
138
141
static struct env {
@@ -142,10 +145,12 @@ static struct env {
142
145
bool debug ;
143
146
bool quiet ;
144
147
bool force_checkpoints ;
148
+ bool force_reg_invariants ;
145
149
enum resfmt out_fmt ;
146
150
bool show_version ;
147
151
bool comparison_mode ;
148
152
bool replay_mode ;
153
+ int top_n ;
149
154
150
155
int log_level ;
151
156
int log_size ;
@@ -210,15 +215,18 @@ static const struct argp_option opts[] = {
210
215
{ "log-level" , 'l' , "LEVEL" , 0 , "Verifier log level (default 0 for normal mode, 1 for verbose mode)" },
211
216
{ "log-fixed" , OPT_LOG_FIXED , NULL , 0 , "Disable verifier log rotation" },
212
217
{ "log-size" , OPT_LOG_SIZE , "BYTES" , 0 , "Customize verifier log size (default to 16MB)" },
213
- { "test-states" , 't' , NULL , 0 ,
214
- "Force frequent BPF verifier state checkpointing (set BPF_F_TEST_STATE_FREQ program flag)" },
218
+ { "top-n" , 'n' , "N" , 0 , "Emit only up to first N results." },
215
219
{ "quiet" , 'q' , NULL , 0 , "Quiet mode" },
216
220
{ "emit" , 'e' , "SPEC" , 0 , "Specify stats to be emitted" },
217
221
{ "sort" , 's' , "SPEC" , 0 , "Specify sort order" },
218
222
{ "output-format" , 'o' , "FMT" , 0 , "Result output format (table, csv), default is table." },
219
223
{ "compare" , 'C' , NULL , 0 , "Comparison mode" },
220
224
{ "replay" , 'R' , NULL , 0 , "Replay mode" },
221
225
{ "filter" , 'f' , "FILTER" , 0 , "Filter expressions (or @filename for file with expressions)." },
226
+ { "test-states" , 't' , NULL , 0 ,
227
+ "Force frequent BPF verifier state checkpointing (set BPF_F_TEST_STATE_FREQ program flag)" },
228
+ { "test-reg-invariants" , 'r' , NULL , 0 ,
229
+ "Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" },
222
230
{},
223
231
};
224
232
@@ -290,6 +298,16 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
290
298
case 't' :
291
299
env .force_checkpoints = true;
292
300
break ;
301
+ case 'r' :
302
+ env .force_reg_invariants = true;
303
+ break ;
304
+ case 'n' :
305
+ errno = 0 ;
306
+ env .top_n = strtol (arg , NULL , 10 );
307
+ if (errno ) {
308
+ fprintf (stderr , "invalid top N specifier: %s\n" , arg );
309
+ argp_usage (state );
310
+ }
293
311
case 'C' :
294
312
env .comparison_mode = true;
295
313
break ;
@@ -455,7 +473,8 @@ static struct {
455
473
{ OP_EQ , "=" },
456
474
};
457
475
458
- static bool parse_stat_id_var (const char * name , size_t len , int * id , enum stat_variant * var );
476
+ static bool parse_stat_id_var (const char * name , size_t len , int * id ,
477
+ enum stat_variant * var , bool * is_abs );
459
478
460
479
static int append_filter (struct filter * * filters , int * cnt , const char * str )
461
480
{
@@ -488,13 +507,14 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
488
507
long val ;
489
508
const char * end = str ;
490
509
const char * op_str ;
510
+ bool is_abs ;
491
511
492
512
op_str = operators [i ].op_str ;
493
513
p = strstr (str , op_str );
494
514
if (!p )
495
515
continue ;
496
516
497
- if (!parse_stat_id_var (str , p - str , & id , & var )) {
517
+ if (!parse_stat_id_var (str , p - str , & id , & var , & is_abs )) {
498
518
fprintf (stderr , "Unrecognized stat name in '%s'!\n" , str );
499
519
return - EINVAL ;
500
520
}
@@ -533,6 +553,7 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
533
553
f -> stat_id = id ;
534
554
f -> stat_var = var ;
535
555
f -> op = operators [i ].op_kind ;
556
+ f -> abs = true;
536
557
f -> value = val ;
537
558
538
559
* cnt += 1 ;
@@ -657,7 +678,8 @@ static struct stat_def {
657
678
[MARK_READ_MAX_LEN ] = { "Max mark read length" , {"max_mark_read_len" , "mark_read" }, },
658
679
};
659
680
660
- static bool parse_stat_id_var (const char * name , size_t len , int * id , enum stat_variant * var )
681
+ static bool parse_stat_id_var (const char * name , size_t len , int * id ,
682
+ enum stat_variant * var , bool * is_abs )
661
683
{
662
684
static const char * var_sfxs [] = {
663
685
[VARIANT_A ] = "_a" ,
@@ -667,6 +689,14 @@ static bool parse_stat_id_var(const char *name, size_t len, int *id, enum stat_v
667
689
};
668
690
int i , j , k ;
669
691
692
+ /* |<stat>| means we take absolute value of given stat */
693
+ * is_abs = false;
694
+ if (len > 2 && name [0 ] == '|' && name [len - 1 ] == '|' ) {
695
+ * is_abs = true;
696
+ name += 1 ;
697
+ len -= 2 ;
698
+ }
699
+
670
700
for (i = 0 ; i < ARRAY_SIZE (stat_defs ); i ++ ) {
671
701
struct stat_def * def = & stat_defs [i ];
672
702
size_t alias_len , sfx_len ;
@@ -722,7 +752,7 @@ static bool is_desc_sym(char c)
722
752
static int parse_stat (const char * stat_name , struct stat_specs * specs )
723
753
{
724
754
int id ;
725
- bool has_order = false, is_asc = false;
755
+ bool has_order = false, is_asc = false, is_abs = false ;
726
756
size_t len = strlen (stat_name );
727
757
enum stat_variant var ;
728
758
@@ -737,14 +767,15 @@ static int parse_stat(const char *stat_name, struct stat_specs *specs)
737
767
len -= 1 ;
738
768
}
739
769
740
- if (!parse_stat_id_var (stat_name , len , & id , & var )) {
770
+ if (!parse_stat_id_var (stat_name , len , & id , & var , & is_abs )) {
741
771
fprintf (stderr , "Unrecognized stat name '%s'\n" , stat_name );
742
772
return - ESRCH ;
743
773
}
744
774
745
775
specs -> ids [specs -> spec_cnt ] = id ;
746
776
specs -> variants [specs -> spec_cnt ] = var ;
747
777
specs -> asc [specs -> spec_cnt ] = has_order ? is_asc : stat_defs [id ].asc_by_default ;
778
+ specs -> abs [specs -> spec_cnt ] = is_abs ;
748
779
specs -> spec_cnt ++ ;
749
780
750
781
return 0 ;
@@ -997,6 +1028,8 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
997
1028
998
1029
if (env .force_checkpoints )
999
1030
bpf_program__set_flags (prog , bpf_program__flags (prog ) | BPF_F_TEST_STATE_FREQ );
1031
+ if (env .force_reg_invariants )
1032
+ bpf_program__set_flags (prog , bpf_program__flags (prog ) | BPF_F_TEST_REG_INVARIANTS );
1000
1033
1001
1034
err = bpf_object__load (obj );
1002
1035
env .progs_processed ++ ;
@@ -1103,7 +1136,7 @@ static int process_obj(const char *filename)
1103
1136
}
1104
1137
1105
1138
static int cmp_stat (const struct verif_stats * s1 , const struct verif_stats * s2 ,
1106
- enum stat_id id , bool asc )
1139
+ enum stat_id id , bool asc , bool abs )
1107
1140
{
1108
1141
int cmp = 0 ;
1109
1142
@@ -1124,6 +1157,11 @@ static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
1124
1157
long v1 = s1 -> stats [id ];
1125
1158
long v2 = s2 -> stats [id ];
1126
1159
1160
+ if (abs ) {
1161
+ v1 = v1 < 0 ? - v1 : v1 ;
1162
+ v2 = v2 < 0 ? - v2 : v2 ;
1163
+ }
1164
+
1127
1165
if (v1 != v2 )
1128
1166
cmp = v1 < v2 ? -1 : 1 ;
1129
1167
break ;
@@ -1142,7 +1180,8 @@ static int cmp_prog_stats(const void *v1, const void *v2)
1142
1180
int i , cmp ;
1143
1181
1144
1182
for (i = 0 ; i < env .sort_spec .spec_cnt ; i ++ ) {
1145
- cmp = cmp_stat (s1 , s2 , env .sort_spec .ids [i ], env .sort_spec .asc [i ]);
1183
+ cmp = cmp_stat (s1 , s2 , env .sort_spec .ids [i ],
1184
+ env .sort_spec .asc [i ], env .sort_spec .abs [i ]);
1146
1185
if (cmp != 0 )
1147
1186
return cmp ;
1148
1187
}
@@ -1211,15 +1250,21 @@ static void fetch_join_stat_value(const struct verif_stats_join *s,
1211
1250
1212
1251
static int cmp_join_stat (const struct verif_stats_join * s1 ,
1213
1252
const struct verif_stats_join * s2 ,
1214
- enum stat_id id , enum stat_variant var , bool asc )
1253
+ enum stat_id id , enum stat_variant var ,
1254
+ bool asc , bool abs )
1215
1255
{
1216
1256
const char * str1 = NULL , * str2 = NULL ;
1217
- double v1 , v2 ;
1257
+ double v1 = 0.0 , v2 = 0.0 ;
1218
1258
int cmp = 0 ;
1219
1259
1220
1260
fetch_join_stat_value (s1 , id , var , & str1 , & v1 );
1221
1261
fetch_join_stat_value (s2 , id , var , & str2 , & v2 );
1222
1262
1263
+ if (abs ) {
1264
+ v1 = fabs (v1 );
1265
+ v2 = fabs (v2 );
1266
+ }
1267
+
1223
1268
if (str1 )
1224
1269
cmp = strcmp (str1 , str2 );
1225
1270
else if (v1 != v2 )
@@ -1237,7 +1282,8 @@ static int cmp_join_stats(const void *v1, const void *v2)
1237
1282
cmp = cmp_join_stat (s1 , s2 ,
1238
1283
env .sort_spec .ids [i ],
1239
1284
env .sort_spec .variants [i ],
1240
- env .sort_spec .asc [i ]);
1285
+ env .sort_spec .asc [i ],
1286
+ env .sort_spec .abs [i ]);
1241
1287
if (cmp != 0 )
1242
1288
return cmp ;
1243
1289
}
@@ -1720,6 +1766,9 @@ static bool is_join_stat_filter_matched(struct filter *f, const struct verif_sta
1720
1766
1721
1767
fetch_join_stat_value (stats , f -> stat_id , f -> stat_var , & str , & value );
1722
1768
1769
+ if (f -> abs )
1770
+ value = fabs (value );
1771
+
1723
1772
switch (f -> op ) {
1724
1773
case OP_EQ : return value > f -> value - eps && value < f -> value + eps ;
1725
1774
case OP_NEQ : return value < f -> value - eps || value > f -> value + eps ;
@@ -1766,7 +1815,7 @@ static int handle_comparison_mode(void)
1766
1815
struct stat_specs base_specs = {}, comp_specs = {};
1767
1816
struct stat_specs tmp_sort_spec ;
1768
1817
enum resfmt cur_fmt ;
1769
- int err , i , j , last_idx ;
1818
+ int err , i , j , last_idx , cnt ;
1770
1819
1771
1820
if (env .filename_cnt != 2 ) {
1772
1821
fprintf (stderr , "Comparison mode expects exactly two input CSV files!\n\n" );
@@ -1879,7 +1928,7 @@ static int handle_comparison_mode(void)
1879
1928
env .join_stat_cnt += 1 ;
1880
1929
}
1881
1930
1882
- /* now sort joined results accorsing to sort spec */
1931
+ /* now sort joined results according to sort spec */
1883
1932
qsort (env .join_stats , env .join_stat_cnt , sizeof (* env .join_stats ), cmp_join_stats );
1884
1933
1885
1934
/* for human-readable table output we need to do extra pass to
@@ -1896,16 +1945,22 @@ static int handle_comparison_mode(void)
1896
1945
output_comp_headers (cur_fmt );
1897
1946
1898
1947
last_idx = -1 ;
1948
+ cnt = 0 ;
1899
1949
for (i = 0 ; i < env .join_stat_cnt ; i ++ ) {
1900
1950
const struct verif_stats_join * join = & env .join_stats [i ];
1901
1951
1902
1952
if (!should_output_join_stats (join ))
1903
1953
continue ;
1904
1954
1955
+ if (env .top_n && cnt >= env .top_n )
1956
+ break ;
1957
+
1905
1958
if (cur_fmt == RESFMT_TABLE_CALCLEN )
1906
1959
last_idx = i ;
1907
1960
1908
1961
output_comp_stats (join , cur_fmt , i == last_idx );
1962
+
1963
+ cnt ++ ;
1909
1964
}
1910
1965
1911
1966
if (cur_fmt == RESFMT_TABLE_CALCLEN ) {
@@ -1920,6 +1975,9 @@ static bool is_stat_filter_matched(struct filter *f, const struct verif_stats *s
1920
1975
{
1921
1976
long value = stats -> stats [f -> stat_id ];
1922
1977
1978
+ if (f -> abs )
1979
+ value = value < 0 ? - value : value ;
1980
+
1923
1981
switch (f -> op ) {
1924
1982
case OP_EQ : return value == f -> value ;
1925
1983
case OP_NEQ : return value != f -> value ;
@@ -1964,7 +2022,7 @@ static bool should_output_stats(const struct verif_stats *stats)
1964
2022
static void output_prog_stats (void )
1965
2023
{
1966
2024
const struct verif_stats * stats ;
1967
- int i , last_stat_idx = 0 ;
2025
+ int i , last_stat_idx = 0 , cnt = 0 ;
1968
2026
1969
2027
if (env .out_fmt == RESFMT_TABLE ) {
1970
2028
/* calculate column widths */
@@ -1984,7 +2042,10 @@ static void output_prog_stats(void)
1984
2042
stats = & env .prog_stats [i ];
1985
2043
if (!should_output_stats (stats ))
1986
2044
continue ;
2045
+ if (env .top_n && cnt >= env .top_n )
2046
+ break ;
1987
2047
output_stats (stats , env .out_fmt , i == last_stat_idx );
2048
+ cnt ++ ;
1988
2049
}
1989
2050
}
1990
2051
0 commit comments