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