@@ -49,6 +49,7 @@ enum stat_id {
49
49
STACK ,
50
50
PROG_TYPE ,
51
51
ATTACH_TYPE ,
52
+ MEMORY_PEAK ,
52
53
53
54
FILE_NAME ,
54
55
PROG_NAME ,
@@ -208,6 +209,9 @@ static struct env {
208
209
int top_src_lines ;
209
210
struct var_preset * presets ;
210
211
int npresets ;
212
+ char orig_cgroup [PATH_MAX ];
213
+ char stat_cgroup [PATH_MAX ];
214
+ int memory_peak_fd ;
211
215
} env ;
212
216
213
217
static int libbpf_print_fn (enum libbpf_print_level level , const char * format , va_list args )
@@ -219,6 +223,22 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va
219
223
return vfprintf (stderr , format , args );
220
224
}
221
225
226
+ #define log_errno (fmt , ...) log_errno_aux(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
227
+
228
+ __printf (3 , 4 )
229
+ static int log_errno_aux (const char * file , int line , const char * fmt , ...)
230
+ {
231
+ int err = - errno ;
232
+ va_list ap ;
233
+
234
+ va_start (ap , fmt );
235
+ fprintf (stderr , "%s:%d: " , file , line );
236
+ vfprintf (stderr , fmt , ap );
237
+ fprintf (stderr , " failed with error '%s'.\n" , strerror (errno ));
238
+ va_end (ap );
239
+ return err ;
240
+ }
241
+
222
242
#ifndef VERISTAT_VERSION
223
243
#define VERISTAT_VERSION "<kernel>"
224
244
#endif
@@ -734,13 +754,13 @@ static int append_file_from_file(const char *path)
734
754
}
735
755
736
756
static const struct stat_specs default_csv_output_spec = {
737
- .spec_cnt = 14 ,
757
+ .spec_cnt = 15 ,
738
758
.ids = {
739
759
FILE_NAME , PROG_NAME , VERDICT , DURATION ,
740
760
TOTAL_INSNS , TOTAL_STATES , PEAK_STATES ,
741
761
MAX_STATES_PER_INSN , MARK_READ_MAX_LEN ,
742
762
SIZE , JITED_SIZE , PROG_TYPE , ATTACH_TYPE ,
743
- STACK ,
763
+ STACK , MEMORY_PEAK ,
744
764
},
745
765
};
746
766
@@ -781,6 +801,7 @@ static struct stat_def {
781
801
[STACK ] = {"Stack depth" , {"stack_depth" , "stack" }, },
782
802
[PROG_TYPE ] = { "Program type" , {"prog_type" }, },
783
803
[ATTACH_TYPE ] = { "Attach type" , {"attach_type" , }, },
804
+ [MEMORY_PEAK ] = { "Peak memory (MiB)" , {"mem_peak" , }, },
784
805
};
785
806
786
807
static bool parse_stat_id_var (const char * name , size_t len , int * id ,
@@ -1279,16 +1300,214 @@ static int max_verifier_log_size(void)
1279
1300
return log_size ;
1280
1301
}
1281
1302
1303
+ static bool output_stat_enabled (int id )
1304
+ {
1305
+ int i ;
1306
+
1307
+ for (i = 0 ; i < env .output_spec .spec_cnt ; i ++ )
1308
+ if (env .output_spec .ids [i ] == id )
1309
+ return true;
1310
+ return false;
1311
+ }
1312
+
1313
+ __printf (2 , 3 )
1314
+ static int write_one_line (const char * file , const char * fmt , ...)
1315
+ {
1316
+ int err , saved_errno ;
1317
+ va_list ap ;
1318
+ FILE * f ;
1319
+
1320
+ f = fopen (file , "w" );
1321
+ if (!f )
1322
+ return -1 ;
1323
+
1324
+ va_start (ap , fmt );
1325
+ errno = 0 ;
1326
+ err = vfprintf (f , fmt , ap );
1327
+ saved_errno = errno ;
1328
+ va_end (ap );
1329
+ fclose (f );
1330
+ errno = saved_errno ;
1331
+ return err < 0 ? -1 : 0 ;
1332
+ }
1333
+
1334
+ __scanf (3 , 4 )
1335
+ static int scanf_one_line (const char * file , int fields_expected , const char * fmt , ...)
1336
+ {
1337
+ int res = 0 , saved_errno = 0 ;
1338
+ char * line = NULL ;
1339
+ size_t line_len ;
1340
+ va_list ap ;
1341
+ FILE * f ;
1342
+
1343
+ f = fopen (file , "r" );
1344
+ if (!f )
1345
+ return -1 ;
1346
+
1347
+ va_start (ap , fmt );
1348
+ while (getline (& line , & line_len , f ) > 0 ) {
1349
+ res = vsscanf (line , fmt , ap );
1350
+ if (res == fields_expected )
1351
+ goto out ;
1352
+ }
1353
+ if (ferror (f )) {
1354
+ saved_errno = errno ;
1355
+ res = -1 ;
1356
+ }
1357
+
1358
+ out :
1359
+ va_end (ap );
1360
+ free (line );
1361
+ fclose (f );
1362
+ errno = saved_errno ;
1363
+ return res ;
1364
+ }
1365
+
1366
+ static void destroy_stat_cgroup (void )
1367
+ {
1368
+ char buf [PATH_MAX ];
1369
+ int err ;
1370
+
1371
+ close (env .memory_peak_fd );
1372
+
1373
+ if (env .orig_cgroup [0 ]) {
1374
+ snprintf (buf , sizeof (buf ), "%s/cgroup.procs" , env .orig_cgroup );
1375
+ err = write_one_line (buf , "%d\n" , getpid ());
1376
+ if (err < 0 )
1377
+ log_errno ("moving self to original cgroup %s\n" , env .orig_cgroup );
1378
+ }
1379
+
1380
+ if (env .stat_cgroup [0 ]) {
1381
+ err = rmdir (env .stat_cgroup );
1382
+ if (err < 0 )
1383
+ log_errno ("deletion of cgroup %s" , env .stat_cgroup );
1384
+ }
1385
+
1386
+ env .memory_peak_fd = -1 ;
1387
+ env .orig_cgroup [0 ] = 0 ;
1388
+ env .stat_cgroup [0 ] = 0 ;
1389
+ }
1390
+
1391
+ /*
1392
+ * Creates a cgroup at /sys/fs/cgroup/veristat-accounting-<pid>,
1393
+ * moves current process to this cgroup.
1394
+ */
1395
+ static void create_stat_cgroup (void )
1396
+ {
1397
+ char cgroup_fs_mount [4096 ];
1398
+ char buf [4096 ];
1399
+ int err ;
1400
+
1401
+ env .memory_peak_fd = -1 ;
1402
+
1403
+ if (!output_stat_enabled (MEMORY_PEAK ))
1404
+ return ;
1405
+
1406
+ err = scanf_one_line ("/proc/self/mounts" , 2 , "%*s %4095s cgroup2 %s" ,
1407
+ cgroup_fs_mount , buf );
1408
+ if (err != 2 ) {
1409
+ if (err < 0 )
1410
+ log_errno ("reading /proc/self/mounts" );
1411
+ else if (!env .quiet )
1412
+ fprintf (stderr , "Can't find cgroupfs v2 mount point.\n" );
1413
+ goto err_out ;
1414
+ }
1415
+
1416
+ /* cgroup-v2.rst promises the line "0::<group>" for cgroups v2 */
1417
+ err = scanf_one_line ("/proc/self/cgroup" , 1 , "0::%4095s" , buf );
1418
+ if (err != 1 ) {
1419
+ if (err < 0 )
1420
+ log_errno ("reading /proc/self/cgroup" );
1421
+ else if (!env .quiet )
1422
+ fprintf (stderr , "Can't infer veristat process cgroup." );
1423
+ goto err_out ;
1424
+ }
1425
+
1426
+ snprintf (env .orig_cgroup , sizeof (env .orig_cgroup ), "%s/%s" , cgroup_fs_mount , buf );
1427
+
1428
+ snprintf (buf , sizeof (buf ), "%s/veristat-accounting-%d" , cgroup_fs_mount , getpid ());
1429
+ err = mkdir (buf , 0777 );
1430
+ if (err < 0 ) {
1431
+ log_errno ("creation of cgroup %s" , buf );
1432
+ goto err_out ;
1433
+ }
1434
+ strcpy (env .stat_cgroup , buf );
1435
+
1436
+ snprintf (buf , sizeof (buf ), "%s/cgroup.procs" , env .stat_cgroup );
1437
+ err = write_one_line (buf , "%d\n" , getpid ());
1438
+ if (err < 0 ) {
1439
+ log_errno ("entering cgroup %s" , buf );
1440
+ goto err_out ;
1441
+ }
1442
+
1443
+ snprintf (buf , sizeof (buf ), "%s/memory.peak" , env .stat_cgroup );
1444
+ env .memory_peak_fd = open (buf , O_RDWR | O_APPEND );
1445
+ if (env .memory_peak_fd < 0 ) {
1446
+ log_errno ("opening %s" , buf );
1447
+ goto err_out ;
1448
+ }
1449
+
1450
+ return ;
1451
+
1452
+ err_out :
1453
+ if (!env .quiet )
1454
+ fprintf (stderr , "Memory usage metric unavailable.\n" );
1455
+ destroy_stat_cgroup ();
1456
+ }
1457
+
1458
+ /* Current value of /sys/fs/cgroup/veristat-accounting-<pid>/memory.peak */
1459
+ static long cgroup_memory_peak (void )
1460
+ {
1461
+ long err , memory_peak ;
1462
+ char buf [32 ];
1463
+
1464
+ if (env .memory_peak_fd < 0 )
1465
+ return -1 ;
1466
+
1467
+ err = pread (env .memory_peak_fd , buf , sizeof (buf ) - 1 , 0 );
1468
+ if (err <= 0 ) {
1469
+ log_errno ("pread(%s/memory.peak)" , env .stat_cgroup );
1470
+ return -1 ;
1471
+ }
1472
+
1473
+ buf [err ] = 0 ;
1474
+ errno = 0 ;
1475
+ memory_peak = strtoll (buf , NULL , 10 );
1476
+ if (errno ) {
1477
+ log_errno ("%s/memory.peak:strtoll(%s)" , env .stat_cgroup , buf );
1478
+ return -1 ;
1479
+ }
1480
+
1481
+ return memory_peak ;
1482
+ }
1483
+
1484
+ static int reset_stat_cgroup (void )
1485
+ {
1486
+ char buf [] = "r\n" ;
1487
+ int err ;
1488
+
1489
+ if (env .memory_peak_fd < 0 )
1490
+ return -1 ;
1491
+
1492
+ err = pwrite (env .memory_peak_fd , buf , sizeof (buf ), 0 );
1493
+ if (err <= 0 ) {
1494
+ log_errno ("pwrite(%s/memory.peak)" , env .stat_cgroup );
1495
+ return -1 ;
1496
+ }
1497
+ return 0 ;
1498
+ }
1499
+
1282
1500
static int process_prog (const char * filename , struct bpf_object * obj , struct bpf_program * prog )
1283
1501
{
1284
1502
const char * base_filename = basename (strdupa (filename ));
1285
1503
const char * prog_name = bpf_program__name (prog );
1504
+ long mem_peak_a , mem_peak_b , mem_peak = -1 ;
1286
1505
char * buf ;
1287
1506
int buf_sz , log_level ;
1288
1507
struct verif_stats * stats ;
1289
1508
struct bpf_prog_info info ;
1290
1509
__u32 info_len = sizeof (info );
1291
- int err = 0 ;
1510
+ int err = 0 , cgroup_err ;
1292
1511
void * tmp ;
1293
1512
int fd ;
1294
1513
@@ -1333,7 +1552,15 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
1333
1552
if (env .force_reg_invariants )
1334
1553
bpf_program__set_flags (prog , bpf_program__flags (prog ) | BPF_F_TEST_REG_INVARIANTS );
1335
1554
1336
- err = bpf_object__load (obj );
1555
+ err = bpf_object__prepare (obj );
1556
+ if (!err ) {
1557
+ cgroup_err = reset_stat_cgroup ();
1558
+ mem_peak_a = cgroup_memory_peak ();
1559
+ err = bpf_object__load (obj );
1560
+ mem_peak_b = cgroup_memory_peak ();
1561
+ if (!cgroup_err && mem_peak_a >= 0 && mem_peak_b >= 0 )
1562
+ mem_peak = mem_peak_b - mem_peak_a ;
1563
+ }
1337
1564
env .progs_processed ++ ;
1338
1565
1339
1566
stats -> file_name = strdup (base_filename );
@@ -1342,6 +1569,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
1342
1569
stats -> stats [SIZE ] = bpf_program__insn_cnt (prog );
1343
1570
stats -> stats [PROG_TYPE ] = bpf_program__type (prog );
1344
1571
stats -> stats [ATTACH_TYPE ] = bpf_program__expected_attach_type (prog );
1572
+ stats -> stats [MEMORY_PEAK ] = mem_peak < 0 ? -1 : mem_peak / (1024 * 1024 );
1345
1573
1346
1574
memset (& info , 0 , info_len );
1347
1575
fd = bpf_program__fd (prog );
@@ -1825,6 +2053,7 @@ static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
1825
2053
case TOTAL_STATES :
1826
2054
case PEAK_STATES :
1827
2055
case MAX_STATES_PER_INSN :
2056
+ case MEMORY_PEAK :
1828
2057
case MARK_READ_MAX_LEN : {
1829
2058
long v1 = s1 -> stats [id ];
1830
2059
long v2 = s2 -> stats [id ];
@@ -2054,6 +2283,7 @@ static void prepare_value(const struct verif_stats *s, enum stat_id id,
2054
2283
case STACK :
2055
2284
case SIZE :
2056
2285
case JITED_SIZE :
2286
+ case MEMORY_PEAK :
2057
2287
* val = s ? s -> stats [id ] : 0 ;
2058
2288
break ;
2059
2289
default :
@@ -2140,6 +2370,7 @@ static int parse_stat_value(const char *str, enum stat_id id, struct verif_stats
2140
2370
case MARK_READ_MAX_LEN :
2141
2371
case SIZE :
2142
2372
case JITED_SIZE :
2373
+ case MEMORY_PEAK :
2143
2374
case STACK : {
2144
2375
long val ;
2145
2376
int err , n ;
@@ -2777,27 +3008,30 @@ static void output_prog_stats(void)
2777
3008
2778
3009
static int handle_verif_mode (void )
2779
3010
{
2780
- int i , err ;
3011
+ int i , err = 0 ;
2781
3012
2782
3013
if (env .filename_cnt == 0 ) {
2783
3014
fprintf (stderr , "Please provide path to BPF object file!\n\n" );
2784
3015
argp_help (& argp , stderr , ARGP_HELP_USAGE , "veristat" );
2785
3016
return - EINVAL ;
2786
3017
}
2787
3018
3019
+ create_stat_cgroup ();
2788
3020
for (i = 0 ; i < env .filename_cnt ; i ++ ) {
2789
3021
err = process_obj (env .filenames [i ]);
2790
3022
if (err ) {
2791
3023
fprintf (stderr , "Failed to process '%s': %d\n" , env .filenames [i ], err );
2792
- return err ;
3024
+ goto out ;
2793
3025
}
2794
3026
}
2795
3027
2796
3028
qsort (env .prog_stats , env .prog_stat_cnt , sizeof (* env .prog_stats ), cmp_prog_stats );
2797
3029
2798
3030
output_prog_stats ();
2799
3031
2800
- return 0 ;
3032
+ out :
3033
+ destroy_stat_cgroup ();
3034
+ return err ;
2801
3035
}
2802
3036
2803
3037
static int handle_replay_mode (void )
0 commit comments