Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf lock: Add 'contention' subcommand

The 'perf lock contention' processes the lock contention events and
displays the result like perf lock report. Right now, there's not
much difference between the two but the lock contention specific
features will come soon.

$ perf lock contention
contended total wait max wait avg wait type caller

238 1.41 ms 29.20 us 5.94 us spinlock update_blocked_averages+0x4c
1 902.08 us 902.08 us 902.08 us rwsem:R do_user_addr_fault+0x1dd
81 330.30 us 17.24 us 4.08 us spinlock _nohz_idle_balance+0x172
2 89.54 us 61.26 us 44.77 us spinlock do_anonymous_page+0x16d
24 78.36 us 12.27 us 3.27 us mutex pipe_read+0x56
2 71.58 us 59.56 us 35.79 us spinlock __handle_mm_fault+0x6aa
6 25.68 us 6.89 us 4.28 us spinlock do_idle+0x28d
1 18.46 us 18.46 us 18.46 us rtmutex exec_fw_cmd+0x21b
3 15.25 us 6.26 us 5.08 us spinlock tick_do_update_jiffies64+0x2c

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Waiman Long <longman@redhat.com>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20220725183124.368304-4-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
528b9cab f9c695a2

+214 -2
+3 -1
tools/perf/Documentation/perf-lock.txt
··· 8 8 SYNOPSIS 9 9 -------- 10 10 [verse] 11 - 'perf lock' {record|report|script|info} 11 + 'perf lock' {record|report|script|info|contention} 12 12 13 13 DESCRIPTION 14 14 ----------- ··· 26 26 27 27 'perf lock info' shows metadata like threads or addresses 28 28 of lock instances. 29 + 30 + 'perf lock contention' shows contention statistics. 29 31 30 32 COMMON OPTIONS 31 33 --------------
+211 -1
tools/perf/builtin-lock.c
··· 145 145 */ 146 146 #define CONTENTION_STACK_SKIP 3 147 147 148 + /* 149 + * flags for lock:contention_begin 150 + * Imported from include/trace/events/lock.h. 151 + */ 152 + #define LCB_F_SPIN (1U << 0) 153 + #define LCB_F_READ (1U << 1) 154 + #define LCB_F_WRITE (1U << 2) 155 + #define LCB_F_RT (1U << 3) 156 + #define LCB_F_PERCPU (1U << 4) 157 + #define LCB_F_MUTEX (1U << 5) 158 + 159 + 148 160 static u64 sched_text_start; 149 161 static u64 sched_text_end; 150 162 static u64 lock_text_start; ··· 1034 1022 return -1; 1035 1023 } 1036 1024 1025 + static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample) 1026 + { 1027 + struct callchain_cursor *cursor = &callchain_cursor; 1028 + struct thread *thread; 1029 + u64 hash = 0; 1030 + int skip = 0; 1031 + int ret; 1032 + 1033 + thread = machine__findnew_thread(&session->machines.host, 1034 + -1, sample->pid); 1035 + if (thread == NULL) 1036 + return -1; 1037 + 1038 + /* use caller function name from the callchain */ 1039 + ret = thread__resolve_callchain(thread, cursor, evsel, sample, 1040 + NULL, NULL, CONTENTION_STACK_DEPTH); 1041 + thread__put(thread); 1042 + 1043 + if (ret != 0) 1044 + return -1; 1045 + 1046 + callchain_cursor_commit(cursor); 1047 + 1048 + while (true) { 1049 + struct callchain_cursor_node *node; 1050 + 1051 + node = callchain_cursor_current(cursor); 1052 + if (node == NULL) 1053 + break; 1054 + 1055 + /* skip first few entries - for lock functions */ 1056 + if (++skip <= CONTENTION_STACK_SKIP) 1057 + goto next; 1058 + 1059 + if (node->ms.sym && is_lock_function(node->ip)) 1060 + goto next; 1061 + 1062 + hash ^= hash_long((unsigned long)node->ip, 64); 1063 + 1064 + next: 1065 + callchain_cursor_advance(cursor); 1066 + } 1067 + return hash; 1068 + } 1069 + 1037 1070 static int report_lock_contention_begin_event(struct evsel *evsel, 1038 1071 struct perf_sample *sample) 1039 1072 { ··· 1096 1039 key = sample->tid; 1097 1040 break; 1098 1041 case LOCK_AGGR_CALLER: 1042 + key = callchain_id(evsel, sample); 1043 + break; 1099 1044 default: 1100 1045 pr_err("Invalid aggregation mode: %d\n", aggr_mode); 1101 1046 return -EINVAL; ··· 1179 1120 key = sample->tid; 1180 1121 break; 1181 1122 case LOCK_AGGR_CALLER: 1123 + key = callchain_id(evsel, sample); 1124 + break; 1182 1125 default: 1183 1126 pr_err("Invalid aggregation mode: %d\n", aggr_mode); 1184 1127 return -EINVAL; ··· 1243 1182 .contention_begin_event = report_lock_contention_begin_event, 1244 1183 .contention_end_event = report_lock_contention_end_event, 1245 1184 }; 1185 + 1186 + static struct trace_lock_handler contention_lock_ops = { 1187 + .contention_begin_event = report_lock_contention_begin_event, 1188 + .contention_end_event = report_lock_contention_end_event, 1189 + }; 1190 + 1246 1191 1247 1192 static struct trace_lock_handler *trace_handler; 1248 1193 ··· 1495 1428 } 1496 1429 } 1497 1430 1431 + static const char *get_type_str(struct lock_stat *st) 1432 + { 1433 + static const struct { 1434 + unsigned int flags; 1435 + const char *name; 1436 + } table[] = { 1437 + { 0, "semaphore" }, 1438 + { LCB_F_SPIN, "spinlock" }, 1439 + { LCB_F_SPIN | LCB_F_READ, "rwlock:R" }, 1440 + { LCB_F_SPIN | LCB_F_WRITE, "rwlock:W"}, 1441 + { LCB_F_READ, "rwsem:R" }, 1442 + { LCB_F_WRITE, "rwsem:W" }, 1443 + { LCB_F_RT, "rtmutex" }, 1444 + { LCB_F_RT | LCB_F_READ, "rwlock-rt:R" }, 1445 + { LCB_F_RT | LCB_F_WRITE, "rwlock-rt:W"}, 1446 + { LCB_F_PERCPU | LCB_F_READ, "pcpu-sem:R" }, 1447 + { LCB_F_PERCPU | LCB_F_WRITE, "pcpu-sem:W" }, 1448 + { LCB_F_MUTEX, "mutex" }, 1449 + { LCB_F_MUTEX | LCB_F_SPIN, "mutex" }, 1450 + }; 1451 + 1452 + for (unsigned int i = 0; i < ARRAY_SIZE(table); i++) { 1453 + if (table[i].flags == st->flags) 1454 + return table[i].name; 1455 + } 1456 + return "unknown"; 1457 + } 1458 + 1459 + static void sort_contention_result(void) 1460 + { 1461 + sort_result(); 1462 + } 1463 + 1464 + static void print_contention_result(void) 1465 + { 1466 + struct lock_stat *st; 1467 + struct lock_key *key; 1468 + int bad, total; 1469 + 1470 + list_for_each_entry(key, &lock_keys, list) 1471 + pr_info("%*s ", key->len, key->header); 1472 + 1473 + pr_info(" %10s %s\n\n", "type", "caller"); 1474 + 1475 + bad = total = 0; 1476 + while ((st = pop_from_result())) { 1477 + total++; 1478 + if (st->broken) 1479 + bad++; 1480 + 1481 + list_for_each_entry(key, &lock_keys, list) { 1482 + key->print(key, st); 1483 + pr_info(" "); 1484 + } 1485 + 1486 + pr_info(" %10s %s\n", get_type_str(st), st->name); 1487 + } 1488 + 1489 + print_bad_events(bad, total); 1490 + } 1491 + 1498 1492 static const struct evsel_str_handler lock_tracepoints[] = { 1499 1493 { "lock:lock_acquire", evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */ 1500 1494 { "lock:lock_acquired", evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */ ··· 1635 1507 perf_session__delete(session); 1636 1508 return err; 1637 1509 } 1510 + 1511 + static int __cmd_contention(void) 1512 + { 1513 + int err = -EINVAL; 1514 + struct perf_tool eops = { 1515 + .sample = process_sample_event, 1516 + .comm = perf_event__process_comm, 1517 + .mmap = perf_event__process_mmap, 1518 + .ordered_events = true, 1519 + }; 1520 + struct perf_data data = { 1521 + .path = input_name, 1522 + .mode = PERF_DATA_MODE_READ, 1523 + .force = force, 1524 + }; 1525 + 1526 + session = perf_session__new(&data, &eops); 1527 + if (IS_ERR(session)) { 1528 + pr_err("Initializing perf session failed\n"); 1529 + return PTR_ERR(session); 1530 + } 1531 + 1532 + /* for lock function check */ 1533 + symbol_conf.sort_by_name = true; 1534 + symbol__init(&session->header.env); 1535 + 1536 + if (!perf_session__has_traces(session, "lock record")) 1537 + goto out_delete; 1538 + 1539 + if (!evlist__find_evsel_by_str(session->evlist, "lock:contention_begin")) { 1540 + pr_err("lock contention evsel not found\n"); 1541 + goto out_delete; 1542 + } 1543 + 1544 + if (perf_session__set_tracepoints_handlers(session, contention_tracepoints)) { 1545 + pr_err("Initializing perf session tracepoint handlers failed\n"); 1546 + goto out_delete; 1547 + } 1548 + 1549 + if (setup_output_field("contended,wait_total,wait_max,avg_wait")) 1550 + goto out_delete; 1551 + 1552 + sort_key = "wait_total"; 1553 + if (select_key()) 1554 + goto out_delete; 1555 + 1556 + aggr_mode = LOCK_AGGR_CALLER; 1557 + 1558 + err = perf_session__process_events(session); 1559 + if (err) 1560 + goto out_delete; 1561 + 1562 + setup_pager(); 1563 + 1564 + sort_contention_result(); 1565 + print_contention_result(); 1566 + 1567 + out_delete: 1568 + perf_session__delete(session); 1569 + return err; 1570 + } 1571 + 1638 1572 1639 1573 static int __cmd_record(int argc, const char **argv) 1640 1574 { ··· 1816 1626 OPT_PARENT(lock_options) 1817 1627 }; 1818 1628 1629 + const struct option contention_options[] = { 1630 + OPT_PARENT(lock_options) 1631 + }; 1632 + 1819 1633 const char * const info_usage[] = { 1820 1634 "perf lock info [<options>]", 1821 1635 NULL 1822 1636 }; 1823 1637 const char *const lock_subcommands[] = { "record", "report", "script", 1824 - "info", NULL }; 1638 + "info", "contention", 1639 + "contention", NULL }; 1825 1640 const char *lock_usage[] = { 1826 1641 NULL, 1827 1642 NULL 1828 1643 }; 1829 1644 const char * const report_usage[] = { 1830 1645 "perf lock report [<options>]", 1646 + NULL 1647 + }; 1648 + const char * const contention_usage[] = { 1649 + "perf lock contention [<options>]", 1831 1650 NULL 1832 1651 }; 1833 1652 unsigned int i; ··· 1874 1675 /* recycling report_lock_ops */ 1875 1676 trace_handler = &report_lock_ops; 1876 1677 rc = __cmd_report(true); 1678 + } else if (strlen(argv[0]) > 2 && strstarts("contention", argv[0])) { 1679 + trace_handler = &contention_lock_ops; 1680 + if (argc) { 1681 + argc = parse_options(argc, argv, contention_options, 1682 + contention_usage, 0); 1683 + if (argc) { 1684 + usage_with_options(contention_usage, 1685 + contention_options); 1686 + } 1687 + } 1688 + rc = __cmd_contention(); 1877 1689 } else { 1878 1690 usage_with_options(lock_usage, lock_options); 1879 1691 }