2727#include "util/map.h"
2828#include "util/util.h"
2929
30+ #include <stdio.h>
3031#include <sys/types.h>
3132#include <sys/prctl.h>
3233#include <semaphore.h>
@@ -65,6 +66,8 @@ static int max_stack_depth = CONTENTION_STACK_DEPTH;
6566static int stack_skip = CONTENTION_STACK_SKIP ;
6667static int print_nr_entries = INT_MAX / 2 ;
6768static LIST_HEAD (callstack_filters );
69+ static const char * output_name = NULL ;
70+ static FILE * lock_output ;
6871
6972struct callstack_filter {
7073 struct list_head list ;
@@ -227,26 +230,26 @@ static void lock_stat_key_print_time(unsigned long long nsec, int len)
227230
228231 /* for CSV output */
229232 if (len == 0 ) {
230- pr_info ( "%llu" , nsec );
233+ fprintf ( lock_output , "%llu" , nsec );
231234 return ;
232235 }
233236
234237 for (int i = 0 ; table [i ].unit ; i ++ ) {
235238 if (nsec < table [i ].base )
236239 continue ;
237240
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 );
239242 return ;
240243 }
241244
242- pr_info ( "%*llu %s" , len - 3 , nsec , "ns" );
245+ fprintf ( lock_output , "%*llu %s" , len - 3 , nsec , "ns" );
243246}
244247
245248#define PRINT_KEY (member ) \
246249static void lock_stat_key_print_ ## member(struct lock_key *key, \
247250 struct lock_stat *ls) \
248251{ \
249- pr_info( "%*llu", key->len, (unsigned long long)ls->member); \
252+ fprintf(lock_output, "%*llu", key->len, (unsigned long long)ls->member);\
250253}
251254
252255#define PRINT_TIME (member ) \
@@ -1335,12 +1338,12 @@ static void print_bad_events(int bad, int total)
13351338 if (quiet || total == 0 || (broken == 0 && verbose <= 0 ))
13361339 return ;
13371340
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" );
13421345 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 ]);
13441347}
13451348
13461349/* TODO: various way to print, coloring, nano or milli sec */
@@ -1352,10 +1355,10 @@ static void print_result(void)
13521355 int bad , total , printed ;
13531356
13541357 if (!quiet ) {
1355- pr_info ( "%20s " , "Name" );
1358+ fprintf ( lock_output , "%20s " , "Name" );
13561359 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" );
13591362 }
13601363
13611364 bad = total = printed = 0 ;
@@ -1380,22 +1383,22 @@ static void print_result(void)
13801383 name = thread__comm_str (t );
13811384 }
13821385
1383- pr_info ( "%20s " , name );
1386+ fprintf ( lock_output , "%20s " , name );
13841387 } else {
13851388 strncpy (cut_name , st -> name , 16 );
13861389 cut_name [16 ] = '.' ;
13871390 cut_name [17 ] = '.' ;
13881391 cut_name [18 ] = '.' ;
13891392 cut_name [19 ] = '\0' ;
13901393 /* cut off name for saving output style */
1391- pr_info ( "%20s " , cut_name );
1394+ fprintf ( lock_output , "%20s " , cut_name );
13921395 }
13931396
13941397 list_for_each_entry (key , & lock_keys , list ) {
13951398 key -> print (key , st );
1396- pr_info ( " " );
1399+ fprintf ( lock_output , " " );
13971400 }
1398- pr_info ( "\n" );
1401+ fprintf ( lock_output , "\n" );
13991402
14001403 if (++ printed >= print_nr_entries )
14011404 break ;
@@ -1412,13 +1415,13 @@ static void dump_threads(void)
14121415 struct rb_node * node ;
14131416 struct thread * t ;
14141417
1415- pr_info ( "%10s: comm\n" , "Thread ID" );
1418+ fprintf ( lock_output , "%10s: comm\n" , "Thread ID" );
14161419
14171420 node = rb_first (& thread_stats );
14181421 while (node ) {
14191422 st = container_of (node , struct thread_stat , rb );
14201423 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 ));
14221425 node = rb_next (node );
14231426 thread__put (t );
14241427 }
@@ -1444,15 +1447,15 @@ static void dump_map(void)
14441447 unsigned int i ;
14451448 struct lock_stat * st ;
14461449
1447- pr_info ( "Address of instance: name of class\n" );
1450+ fprintf ( lock_output , "Address of instance: name of class\n" );
14481451 for (i = 0 ; i < LOCKHASH_SIZE ; i ++ ) {
14491452 hlist_for_each_entry (st , & lockhash_table [i ], hash_entry ) {
14501453 insert_to_result (st , compare_maps );
14511454 }
14521455 }
14531456
14541457 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 );
14561459}
14571460
14581461static int dump_info (void )
@@ -1637,18 +1640,18 @@ static void print_header_stdio(void)
16371640 struct lock_key * key ;
16381641
16391642 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 );
16411644
16421645 switch (aggr_mode ) {
16431646 case LOCK_AGGR_TASK :
1644- pr_info ( " %10s %s\n\n" , "pid" ,
1647+ fprintf ( lock_output , " %10s %s\n\n" , "pid" ,
16451648 show_lock_owner ? "owner" : "comm" );
16461649 break ;
16471650 case LOCK_AGGR_CALLER :
1648- pr_info ( " %10s %s\n\n" , "type" , "caller" );
1651+ fprintf ( lock_output , " %10s %s\n\n" , "type" , "caller" );
16491652 break ;
16501653 case LOCK_AGGR_ADDR :
1651- pr_info ( " %16s %s\n\n" , "address" , "symbol" );
1654+ fprintf ( lock_output , " %16s %s\n\n" , "address" , "symbol" );
16521655 break ;
16531656 default :
16541657 break ;
@@ -1659,23 +1662,23 @@ static void print_header_csv(const char *sep)
16591662{
16601663 struct lock_key * key ;
16611664
1662- pr_info ( "# output: " );
1665+ fprintf ( lock_output , "# output: " );
16631666 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 );
16651668
16661669 switch (aggr_mode ) {
16671670 case LOCK_AGGR_TASK :
1668- pr_info ( "%s%s %s\n" , "pid" , sep ,
1671+ fprintf ( lock_output , "%s%s %s\n" , "pid" , sep ,
16691672 show_lock_owner ? "owner" : "comm" );
16701673 break ;
16711674 case LOCK_AGGR_CALLER :
1672- pr_info ( "%s%s %s" , "type" , sep , "caller" );
1675+ fprintf ( lock_output , "%s%s %s" , "type" , sep , "caller" );
16731676 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" );
16761679 break ;
16771680 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" );
16791682 break ;
16801683 default :
16811684 break ;
@@ -1700,21 +1703,21 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
17001703
17011704 list_for_each_entry (key , & lock_keys , list ) {
17021705 key -> print (key , st );
1703- pr_info ( " " );
1706+ fprintf ( lock_output , " " );
17041707 }
17051708
17061709 switch (aggr_mode ) {
17071710 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 );
17091712 break ;
17101713 case LOCK_AGGR_TASK :
17111714 pid = st -> addr ;
17121715 t = perf_session__findnew (session , pid );
1713- pr_info ( " %10d %s\n" ,
1716+ fprintf ( lock_output , " %10d %s\n" ,
17141717 pid , pid == -1 ? "Unknown" : thread__comm_str (t ));
17151718 break ;
17161719 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 ,
17181721 st -> name , get_type_name (st -> flags ));
17191722 break ;
17201723 default :
@@ -1734,7 +1737,7 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
17341737 ip = st -> callstack [i ];
17351738 sym = machine__find_kernel_symbol (con -> machine , ip , & kmap );
17361739 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 );
17381741 }
17391742 }
17401743}
@@ -1748,22 +1751,23 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
17481751
17491752 list_for_each_entry (key , & lock_keys , list ) {
17501753 key -> print (key , st );
1751- pr_info ( "%s " , sep );
1754+ fprintf ( lock_output , "%s " , sep );
17521755 }
17531756
17541757 switch (aggr_mode ) {
17551758 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 );
17571760 if (verbose <= 0 )
1758- pr_info ( "\n" );
1761+ fprintf ( lock_output , "\n" );
17591762 break ;
17601763 case LOCK_AGGR_TASK :
17611764 pid = st -> addr ;
17621765 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 ));
17641768 break ;
17651769 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 ,
17671771 st -> name , sep , get_type_name (st -> flags ));
17681772 break ;
17691773 default :
@@ -1783,9 +1787,9 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
17831787 ip = st -> callstack [i ];
17841788 sym = machine__find_kernel_symbol (con -> machine , ip , & kmap );
17851789 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 );
17871791 }
1788- pr_info ( "\n" );
1792+ fprintf ( lock_output , "\n" );
17891793 }
17901794}
17911795
@@ -1809,15 +1813,15 @@ static void print_footer_stdio(int total, int bad, struct lock_contention_fails
18091813 return ;
18101814
18111815 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 );
18151819
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 );
18211825}
18221826
18231827static 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
18311835 return ;
18321836
18331837 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 );
18351839
18361840 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 );
18411845 } else {
18421846 int i ;
18431847 const char * name [4 ] = { "acquire" , "acquired" , "contended" , "release" };
18441848
18451849 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 ]);
18471851 }
1848- pr_info ( "\n" );
1852+ fprintf ( lock_output , "\n" );
18491853}
18501854
18511855static 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
24272431 return ret ;
24282432}
24292433
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+
24302452int cmd_lock (int argc , const char * * argv )
24312453{
24322454 const struct option lock_options [] = {
24332455 OPT_STRING ('i' , "input" , & input_name , "file" , "input file name" ),
2456+ OPT_CALLBACK (0 , "output" , & output_name , "file" , "output file name" , parse_output ),
24342457 OPT_INCR ('v' , "verbose" , & verbose , "be more verbose (show symbol address, etc)" ),
24352458 OPT_BOOLEAN ('D' , "dump-raw-trace" , & dump_trace , "dump raw trace in ASCII" ),
24362459 OPT_BOOLEAN ('f' , "force" , & force , "don't complain, do it" ),
@@ -2530,6 +2553,7 @@ int cmd_lock(int argc, const char **argv)
25302553 for (i = 0 ; i < LOCKHASH_SIZE ; i ++ )
25312554 INIT_HLIST_HEAD (lockhash_table + i );
25322555
2556+ lock_output = stderr ;
25332557 argc = parse_options_subcommand (argc , argv , lock_options , lock_subcommands ,
25342558 lock_usage , PARSE_OPT_STOP_AT_NON_OPTION );
25352559 if (!argc )
0 commit comments