27
27
#include "util/map.h"
28
28
#include "util/util.h"
29
29
30
+ #include <stdio.h>
30
31
#include <sys/types.h>
31
32
#include <sys/prctl.h>
32
33
#include <semaphore.h>
@@ -65,6 +66,8 @@ static int max_stack_depth = CONTENTION_STACK_DEPTH;
65
66
static int stack_skip = CONTENTION_STACK_SKIP ;
66
67
static int print_nr_entries = INT_MAX / 2 ;
67
68
static LIST_HEAD (callstack_filters );
69
+ static const char * output_name = NULL ;
70
+ static FILE * lock_output ;
68
71
69
72
struct callstack_filter {
70
73
struct list_head list ;
@@ -227,26 +230,26 @@ static void lock_stat_key_print_time(unsigned long long nsec, int len)
227
230
228
231
/* for CSV output */
229
232
if (len == 0 ) {
230
- pr_info ( "%llu" , nsec );
233
+ fprintf ( lock_output , "%llu" , nsec );
231
234
return ;
232
235
}
233
236
234
237
for (int i = 0 ; table [i ].unit ; i ++ ) {
235
238
if (nsec < table [i ].base )
236
239
continue ;
237
240
238
- pr_info ( "%*.2f %s" , len - 3 , nsec / table [i ].base , table [i ].unit );
241
+ fprintf ( lock_output , "%*.2f %s" , len - 3 , nsec / table [i ].base , table [i ].unit );
239
242
return ;
240
243
}
241
244
242
- pr_info ( "%*llu %s" , len - 3 , nsec , "ns" );
245
+ fprintf ( lock_output , "%*llu %s" , len - 3 , nsec , "ns" );
243
246
}
244
247
245
248
#define PRINT_KEY (member ) \
246
249
static void lock_stat_key_print_ ## member(struct lock_key *key, \
247
250
struct lock_stat *ls) \
248
251
{ \
249
- pr_info( "%*llu", key->len, (unsigned long long)ls->member); \
252
+ fprintf(lock_output, "%*llu", key->len, (unsigned long long)ls->member);\
250
253
}
251
254
252
255
#define PRINT_TIME (member ) \
@@ -1335,12 +1338,12 @@ static void print_bad_events(int bad, int total)
1335
1338
if (quiet || total == 0 || (broken == 0 && verbose <= 0 ))
1336
1339
return ;
1337
1340
1338
- pr_info ( "\n=== output for debug ===\n\n" );
1339
- pr_info ( "bad: %d, total: %d\n" , bad , total );
1340
- pr_info ( "bad rate: %.2f %%\n" , (double )bad / (double )total * 100 );
1341
- pr_info ( "histogram of events caused bad sequence\n" );
1341
+ fprintf ( lock_output , "\n=== output for debug ===\n\n" );
1342
+ fprintf ( lock_output , "bad: %d, total: %d\n" , bad , total );
1343
+ fprintf ( lock_output , "bad rate: %.2f %%\n" , (double )bad / (double )total * 100 );
1344
+ fprintf ( lock_output , "histogram of events caused bad sequence\n" );
1342
1345
for (i = 0 ; i < BROKEN_MAX ; i ++ )
1343
- pr_info ( " %10s: %d\n" , name [i ], bad_hist [i ]);
1346
+ fprintf ( lock_output , " %10s: %d\n" , name [i ], bad_hist [i ]);
1344
1347
}
1345
1348
1346
1349
/* TODO: various way to print, coloring, nano or milli sec */
@@ -1352,10 +1355,10 @@ static void print_result(void)
1352
1355
int bad , total , printed ;
1353
1356
1354
1357
if (!quiet ) {
1355
- pr_info ( "%20s " , "Name" );
1358
+ fprintf ( lock_output , "%20s " , "Name" );
1356
1359
list_for_each_entry (key , & lock_keys , list )
1357
- pr_info ( "%*s " , key -> len , key -> header );
1358
- pr_info ( "\n\n" );
1360
+ fprintf ( lock_output , "%*s " , key -> len , key -> header );
1361
+ fprintf ( lock_output , "\n\n" );
1359
1362
}
1360
1363
1361
1364
bad = total = printed = 0 ;
@@ -1380,22 +1383,22 @@ static void print_result(void)
1380
1383
name = thread__comm_str (t );
1381
1384
}
1382
1385
1383
- pr_info ( "%20s " , name );
1386
+ fprintf ( lock_output , "%20s " , name );
1384
1387
} else {
1385
1388
strncpy (cut_name , st -> name , 16 );
1386
1389
cut_name [16 ] = '.' ;
1387
1390
cut_name [17 ] = '.' ;
1388
1391
cut_name [18 ] = '.' ;
1389
1392
cut_name [19 ] = '\0' ;
1390
1393
/* cut off name for saving output style */
1391
- pr_info ( "%20s " , cut_name );
1394
+ fprintf ( lock_output , "%20s " , cut_name );
1392
1395
}
1393
1396
1394
1397
list_for_each_entry (key , & lock_keys , list ) {
1395
1398
key -> print (key , st );
1396
- pr_info ( " " );
1399
+ fprintf ( lock_output , " " );
1397
1400
}
1398
- pr_info ( "\n" );
1401
+ fprintf ( lock_output , "\n" );
1399
1402
1400
1403
if (++ printed >= print_nr_entries )
1401
1404
break ;
@@ -1412,13 +1415,13 @@ static void dump_threads(void)
1412
1415
struct rb_node * node ;
1413
1416
struct thread * t ;
1414
1417
1415
- pr_info ( "%10s: comm\n" , "Thread ID" );
1418
+ fprintf ( lock_output , "%10s: comm\n" , "Thread ID" );
1416
1419
1417
1420
node = rb_first (& thread_stats );
1418
1421
while (node ) {
1419
1422
st = container_of (node , struct thread_stat , rb );
1420
1423
t = perf_session__findnew (session , st -> tid );
1421
- pr_info ( "%10d: %s\n" , st -> tid , thread__comm_str (t ));
1424
+ fprintf ( lock_output , "%10d: %s\n" , st -> tid , thread__comm_str (t ));
1422
1425
node = rb_next (node );
1423
1426
thread__put (t );
1424
1427
}
@@ -1444,15 +1447,15 @@ static void dump_map(void)
1444
1447
unsigned int i ;
1445
1448
struct lock_stat * st ;
1446
1449
1447
- pr_info ( "Address of instance: name of class\n" );
1450
+ fprintf ( lock_output , "Address of instance: name of class\n" );
1448
1451
for (i = 0 ; i < LOCKHASH_SIZE ; i ++ ) {
1449
1452
hlist_for_each_entry (st , & lockhash_table [i ], hash_entry ) {
1450
1453
insert_to_result (st , compare_maps );
1451
1454
}
1452
1455
}
1453
1456
1454
1457
while ((st = pop_from_result ()))
1455
- pr_info ( " %#llx: %s\n" , (unsigned long long )st -> addr , st -> name );
1458
+ fprintf ( lock_output , " %#llx: %s\n" , (unsigned long long )st -> addr , st -> name );
1456
1459
}
1457
1460
1458
1461
static int dump_info (void )
@@ -1637,18 +1640,18 @@ static void print_header_stdio(void)
1637
1640
struct lock_key * key ;
1638
1641
1639
1642
list_for_each_entry (key , & lock_keys , list )
1640
- pr_info ( "%*s " , key -> len , key -> header );
1643
+ fprintf ( lock_output , "%*s " , key -> len , key -> header );
1641
1644
1642
1645
switch (aggr_mode ) {
1643
1646
case LOCK_AGGR_TASK :
1644
- pr_info ( " %10s %s\n\n" , "pid" ,
1647
+ fprintf ( lock_output , " %10s %s\n\n" , "pid" ,
1645
1648
show_lock_owner ? "owner" : "comm" );
1646
1649
break ;
1647
1650
case LOCK_AGGR_CALLER :
1648
- pr_info ( " %10s %s\n\n" , "type" , "caller" );
1651
+ fprintf ( lock_output , " %10s %s\n\n" , "type" , "caller" );
1649
1652
break ;
1650
1653
case LOCK_AGGR_ADDR :
1651
- pr_info ( " %16s %s\n\n" , "address" , "symbol" );
1654
+ fprintf ( lock_output , " %16s %s\n\n" , "address" , "symbol" );
1652
1655
break ;
1653
1656
default :
1654
1657
break ;
@@ -1659,23 +1662,23 @@ static void print_header_csv(const char *sep)
1659
1662
{
1660
1663
struct lock_key * key ;
1661
1664
1662
- pr_info ( "# output: " );
1665
+ fprintf ( lock_output , "# output: " );
1663
1666
list_for_each_entry (key , & lock_keys , list )
1664
- pr_info ( "%s%s " , key -> header , sep );
1667
+ fprintf ( lock_output , "%s%s " , key -> header , sep );
1665
1668
1666
1669
switch (aggr_mode ) {
1667
1670
case LOCK_AGGR_TASK :
1668
- pr_info ( "%s%s %s\n" , "pid" , sep ,
1671
+ fprintf ( lock_output , "%s%s %s\n" , "pid" , sep ,
1669
1672
show_lock_owner ? "owner" : "comm" );
1670
1673
break ;
1671
1674
case LOCK_AGGR_CALLER :
1672
- pr_info ( "%s%s %s" , "type" , sep , "caller" );
1675
+ fprintf ( lock_output , "%s%s %s" , "type" , sep , "caller" );
1673
1676
if (verbose > 0 )
1674
- pr_info ( "%s %s" , sep , "stacktrace" );
1675
- pr_info ( "\n" );
1677
+ fprintf ( lock_output , "%s %s" , sep , "stacktrace" );
1678
+ fprintf ( lock_output , "\n" );
1676
1679
break ;
1677
1680
case LOCK_AGGR_ADDR :
1678
- pr_info ( "%s%s %s%s %s\n" , "address" , sep , "symbol" , sep , "type" );
1681
+ fprintf ( lock_output , "%s%s %s%s %s\n" , "address" , sep , "symbol" , sep , "type" );
1679
1682
break ;
1680
1683
default :
1681
1684
break ;
@@ -1700,21 +1703,21 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
1700
1703
1701
1704
list_for_each_entry (key , & lock_keys , list ) {
1702
1705
key -> print (key , st );
1703
- pr_info ( " " );
1706
+ fprintf ( lock_output , " " );
1704
1707
}
1705
1708
1706
1709
switch (aggr_mode ) {
1707
1710
case LOCK_AGGR_CALLER :
1708
- pr_info ( " %10s %s\n" , get_type_str (st -> flags ), st -> name );
1711
+ fprintf ( lock_output , " %10s %s\n" , get_type_str (st -> flags ), st -> name );
1709
1712
break ;
1710
1713
case LOCK_AGGR_TASK :
1711
1714
pid = st -> addr ;
1712
1715
t = perf_session__findnew (session , pid );
1713
- pr_info ( " %10d %s\n" ,
1716
+ fprintf ( lock_output , " %10d %s\n" ,
1714
1717
pid , pid == -1 ? "Unknown" : thread__comm_str (t ));
1715
1718
break ;
1716
1719
case LOCK_AGGR_ADDR :
1717
- pr_info ( " %016llx %s (%s)\n" , (unsigned long long )st -> addr ,
1720
+ fprintf ( lock_output , " %016llx %s (%s)\n" , (unsigned long long )st -> addr ,
1718
1721
st -> name , get_type_name (st -> flags ));
1719
1722
break ;
1720
1723
default :
@@ -1734,7 +1737,7 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
1734
1737
ip = st -> callstack [i ];
1735
1738
sym = machine__find_kernel_symbol (con -> machine , ip , & kmap );
1736
1739
get_symbol_name_offset (kmap , sym , ip , buf , sizeof (buf ));
1737
- pr_info ( "\t\t\t%#lx %s\n" , (unsigned long )ip , buf );
1740
+ fprintf ( lock_output , "\t\t\t%#lx %s\n" , (unsigned long )ip , buf );
1738
1741
}
1739
1742
}
1740
1743
}
@@ -1748,22 +1751,23 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
1748
1751
1749
1752
list_for_each_entry (key , & lock_keys , list ) {
1750
1753
key -> print (key , st );
1751
- pr_info ( "%s " , sep );
1754
+ fprintf ( lock_output , "%s " , sep );
1752
1755
}
1753
1756
1754
1757
switch (aggr_mode ) {
1755
1758
case LOCK_AGGR_CALLER :
1756
- pr_info ( "%s%s %s" , get_type_str (st -> flags ), sep , st -> name );
1759
+ fprintf ( lock_output , "%s%s %s" , get_type_str (st -> flags ), sep , st -> name );
1757
1760
if (verbose <= 0 )
1758
- pr_info ( "\n" );
1761
+ fprintf ( lock_output , "\n" );
1759
1762
break ;
1760
1763
case LOCK_AGGR_TASK :
1761
1764
pid = st -> addr ;
1762
1765
t = perf_session__findnew (session , pid );
1763
- pr_info ("%d%s %s\n" , pid , sep , pid == -1 ? "Unknown" : thread__comm_str (t ));
1766
+ fprintf (lock_output , "%d%s %s\n" , pid , sep ,
1767
+ pid == -1 ? "Unknown" : thread__comm_str (t ));
1764
1768
break ;
1765
1769
case LOCK_AGGR_ADDR :
1766
- pr_info ( "%llx%s %s%s %s\n" , (unsigned long long )st -> addr , sep ,
1770
+ fprintf ( lock_output , "%llx%s %s%s %s\n" , (unsigned long long )st -> addr , sep ,
1767
1771
st -> name , sep , get_type_name (st -> flags ));
1768
1772
break ;
1769
1773
default :
@@ -1783,9 +1787,9 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
1783
1787
ip = st -> callstack [i ];
1784
1788
sym = machine__find_kernel_symbol (con -> machine , ip , & kmap );
1785
1789
get_symbol_name_offset (kmap , sym , ip , buf , sizeof (buf ));
1786
- pr_info ( "%s %#lx %s" , i ? ":" : sep , (unsigned long ) ip , buf );
1790
+ fprintf ( lock_output , "%s %#lx %s" , i ? ":" : sep , (unsigned long ) ip , buf );
1787
1791
}
1788
- pr_info ( "\n" );
1792
+ fprintf ( lock_output , "\n" );
1789
1793
}
1790
1794
}
1791
1795
@@ -1809,15 +1813,15 @@ static void print_footer_stdio(int total, int bad, struct lock_contention_fails
1809
1813
return ;
1810
1814
1811
1815
total += broken ;
1812
- pr_info ( "\n=== output for debug ===\n\n" );
1813
- pr_info ( "bad: %d, total: %d\n" , broken , total );
1814
- pr_info ( "bad rate: %.2f %%\n" , ( double ) broken / ( double ) total * 100 );
1816
+ fprintf ( lock_output , "\n=== output for debug ===\n\n" );
1817
+ fprintf ( lock_output , "bad: %d, total: %d\n" , broken , total );
1818
+ fprintf ( lock_output , "bad rate: %.2f %%\n" , 100.0 * broken / total );
1815
1819
1816
- pr_info ( "histogram of failure reasons\n" );
1817
- pr_info ( " %10s: %d\n" , "task" , fails -> task );
1818
- pr_info ( " %10s: %d\n" , "stack" , fails -> stack );
1819
- pr_info ( " %10s: %d\n" , "time" , fails -> time );
1820
- pr_info ( " %10s: %d\n" , "data" , fails -> data );
1820
+ fprintf ( lock_output , "histogram of failure reasons\n" );
1821
+ fprintf ( lock_output , " %10s: %d\n" , "task" , fails -> task );
1822
+ fprintf ( lock_output , " %10s: %d\n" , "stack" , fails -> stack );
1823
+ fprintf ( lock_output , " %10s: %d\n" , "time" , fails -> time );
1824
+ fprintf ( lock_output , " %10s: %d\n" , "data" , fails -> data );
1821
1825
}
1822
1826
1823
1827
static void print_footer_csv (int total , int bad , struct lock_contention_fails * fails ,
@@ -1831,21 +1835,21 @@ static void print_footer_csv(int total, int bad, struct lock_contention_fails *f
1831
1835
return ;
1832
1836
1833
1837
total += bad ;
1834
- pr_info ( "# debug: total=%d%s bad=%d" , total , sep , bad );
1838
+ fprintf ( lock_output , "# debug: total=%d%s bad=%d" , total , sep , bad );
1835
1839
1836
1840
if (use_bpf ) {
1837
- pr_info ( "%s bad_%s=%d" , sep , "task" , fails -> task );
1838
- pr_info ( "%s bad_%s=%d" , sep , "stack" , fails -> stack );
1839
- pr_info ( "%s bad_%s=%d" , sep , "time" , fails -> time );
1840
- pr_info ( "%s bad_%s=%d" , sep , "data" , fails -> data );
1841
+ fprintf ( lock_output , "%s bad_%s=%d" , sep , "task" , fails -> task );
1842
+ fprintf ( lock_output , "%s bad_%s=%d" , sep , "stack" , fails -> stack );
1843
+ fprintf ( lock_output , "%s bad_%s=%d" , sep , "time" , fails -> time );
1844
+ fprintf ( lock_output , "%s bad_%s=%d" , sep , "data" , fails -> data );
1841
1845
} else {
1842
1846
int i ;
1843
1847
const char * name [4 ] = { "acquire" , "acquired" , "contended" , "release" };
1844
1848
1845
1849
for (i = 0 ; i < BROKEN_MAX ; i ++ )
1846
- pr_info ( "%s bad_%s=%d" , sep , name [i ], bad_hist [i ]);
1850
+ fprintf ( lock_output , "%s bad_%s=%d" , sep , name [i ], bad_hist [i ]);
1847
1851
}
1848
- pr_info ( "\n" );
1852
+ fprintf ( lock_output , "\n" );
1849
1853
}
1850
1854
1851
1855
static void print_footer (int total , int bad , struct lock_contention_fails * fails )
@@ -2427,10 +2431,29 @@ static int parse_call_stack(const struct option *opt __maybe_unused, const char
2427
2431
return ret ;
2428
2432
}
2429
2433
2434
+ static int parse_output (const struct option * opt __maybe_unused , const char * str ,
2435
+ int unset __maybe_unused )
2436
+ {
2437
+ const char * * name = (const char * * )opt -> value ;
2438
+
2439
+ if (str == NULL )
2440
+ return -1 ;
2441
+
2442
+ lock_output = fopen (str , "w" );
2443
+ if (lock_output == NULL ) {
2444
+ pr_err ("Cannot open %s\n" , str );
2445
+ return -1 ;
2446
+ }
2447
+
2448
+ * name = str ;
2449
+ return 0 ;
2450
+ }
2451
+
2430
2452
int cmd_lock (int argc , const char * * argv )
2431
2453
{
2432
2454
const struct option lock_options [] = {
2433
2455
OPT_STRING ('i' , "input" , & input_name , "file" , "input file name" ),
2456
+ OPT_CALLBACK (0 , "output" , & output_name , "file" , "output file name" , parse_output ),
2434
2457
OPT_INCR ('v' , "verbose" , & verbose , "be more verbose (show symbol address, etc)" ),
2435
2458
OPT_BOOLEAN ('D' , "dump-raw-trace" , & dump_trace , "dump raw trace in ASCII" ),
2436
2459
OPT_BOOLEAN ('f' , "force" , & force , "don't complain, do it" ),
@@ -2530,6 +2553,7 @@ int cmd_lock(int argc, const char **argv)
2530
2553
for (i = 0 ; i < LOCKHASH_SIZE ; i ++ )
2531
2554
INIT_HLIST_HEAD (lockhash_table + i );
2532
2555
2556
+ lock_output = stderr ;
2533
2557
argc = parse_options_subcommand (argc , argv , lock_options , lock_subcommands ,
2534
2558
lock_usage , PARSE_OPT_STOP_AT_NON_OPTION );
2535
2559
if (!argc )
0 commit comments