Skip to content

Commit f602705

Browse files
committed
perf lock contention: Add --output option
To avoid formatting failures for example in CSV output due to debug messages, add --output option to put the result in a file. Unfortunately the short -o option was taken by the --owner already. $ sudo ./perf lock con -ab --output lock-out.txt -v sleep 1 Looking at the vmlinux_path (8 entries long) symsrc__init: cannot get elf header. Using /proc/kcore for kernel data Using /proc/kallsyms for symbols $ head lock-out.txt contended total wait max wait avg wait type caller 3 76.79 us 26.89 us 25.60 us rwlock:R ep_poll_callback+0x2d 0xffffffff9a23f4b5 _raw_read_lock_irqsave+0x45 0xffffffff99bbd4dd ep_poll_callback+0x2d 0xffffffff999029f3 __wake_up_common+0x73 0xffffffff99902b82 __wake_up_common_lock+0x82 0xffffffff99fa5b1c sock_def_readable+0x3c 0xffffffff9a11521d unix_stream_sendmsg+0x18d 0xffffffff99f9fc9c sock_sendmsg+0x5c Suggested-by: Ian Rogers <[email protected]> Acked-by: Ian Rogers <[email protected]> Cc: Hao Luo <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Song Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Namhyung Kim <[email protected]>
1 parent 69c5c99 commit f602705

File tree

2 files changed

+85
-58
lines changed

2 files changed

+85
-58
lines changed

tools/perf/Documentation/perf-lock.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ COMMON OPTIONS
3636
--input=<file>::
3737
Input file name. (default: perf.data unless stdin is a fifo)
3838

39+
--output=<file>::
40+
Output file name for perf lock contention and report.
41+
3942
-v::
4043
--verbose::
4144
Be more verbose (show symbol address, etc).

tools/perf/builtin-lock.c

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
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;
6566
static int stack_skip = CONTENTION_STACK_SKIP;
6667
static int print_nr_entries = INT_MAX / 2;
6768
static LIST_HEAD(callstack_filters);
69+
static const char *output_name = NULL;
70+
static FILE *lock_output;
6871

6972
struct 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) \
246249
static 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

14581461
static 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

18231827
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
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

18511855
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
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+
24302452
int 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

Comments
 (0)