@@ -145,6 +145,18 @@ static enum {
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 ;
@@ -1022,6 +1034,51 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
1022
1034
return -1 ;
1023
1035
}
1024
1036
1037
+ static u64 callchain_id (struct evsel * evsel , struct perf_sample * sample )
1038
+ {
1039
+ struct callchain_cursor * cursor = & callchain_cursor ;
1040
+ struct thread * thread ;
1041
+ u64 hash = 0 ;
1042
+ int skip = 0 ;
1043
+ int ret ;
1044
+
1045
+ thread = machine__findnew_thread (& session -> machines .host ,
1046
+ -1 , sample -> pid );
1047
+ if (thread == NULL )
1048
+ return -1 ;
1049
+
1050
+ /* use caller function name from the callchain */
1051
+ ret = thread__resolve_callchain (thread , cursor , evsel , sample ,
1052
+ NULL , NULL , CONTENTION_STACK_DEPTH );
1053
+ thread__put (thread );
1054
+
1055
+ if (ret != 0 )
1056
+ return -1 ;
1057
+
1058
+ callchain_cursor_commit (cursor );
1059
+
1060
+ while (true) {
1061
+ struct callchain_cursor_node * node ;
1062
+
1063
+ node = callchain_cursor_current (cursor );
1064
+ if (node == NULL )
1065
+ break ;
1066
+
1067
+ /* skip first few entries - for lock functions */
1068
+ if (++ skip <= CONTENTION_STACK_SKIP )
1069
+ goto next ;
1070
+
1071
+ if (node -> ms .sym && is_lock_function (node -> ip ))
1072
+ goto next ;
1073
+
1074
+ hash ^= hash_long ((unsigned long )node -> ip , 64 );
1075
+
1076
+ next :
1077
+ callchain_cursor_advance (cursor );
1078
+ }
1079
+ return hash ;
1080
+ }
1081
+
1025
1082
static int report_lock_contention_begin_event (struct evsel * evsel ,
1026
1083
struct perf_sample * sample )
1027
1084
{
@@ -1039,6 +1096,8 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
1039
1096
key = sample -> tid ;
1040
1097
break ;
1041
1098
case LOCK_AGGR_CALLER :
1099
+ key = callchain_id (evsel , sample );
1100
+ break ;
1042
1101
default :
1043
1102
pr_err ("Invalid aggregation mode: %d\n" , aggr_mode );
1044
1103
return - EINVAL ;
@@ -1120,6 +1179,8 @@ static int report_lock_contention_end_event(struct evsel *evsel,
1120
1179
key = sample -> tid ;
1121
1180
break ;
1122
1181
case LOCK_AGGR_CALLER :
1182
+ key = callchain_id (evsel , sample );
1183
+ break ;
1123
1184
default :
1124
1185
pr_err ("Invalid aggregation mode: %d\n" , aggr_mode );
1125
1186
return - EINVAL ;
@@ -1183,6 +1244,12 @@ static struct trace_lock_handler report_lock_ops = {
1183
1244
.contention_end_event = report_lock_contention_end_event ,
1184
1245
};
1185
1246
1247
+ static struct trace_lock_handler contention_lock_ops = {
1248
+ .contention_begin_event = report_lock_contention_begin_event ,
1249
+ .contention_end_event = report_lock_contention_end_event ,
1250
+ };
1251
+
1252
+
1186
1253
static struct trace_lock_handler * trace_handler ;
1187
1254
1188
1255
static int evsel__process_lock_acquire (struct evsel * evsel , struct perf_sample * sample )
@@ -1428,6 +1495,67 @@ static void sort_result(void)
1428
1495
}
1429
1496
}
1430
1497
1498
+ static const char * get_type_str (struct lock_stat * st )
1499
+ {
1500
+ static const struct {
1501
+ unsigned int flags ;
1502
+ const char * name ;
1503
+ } table [] = {
1504
+ { 0 , "semaphore" },
1505
+ { LCB_F_SPIN , "spinlock" },
1506
+ { LCB_F_SPIN | LCB_F_READ , "rwlock:R" },
1507
+ { LCB_F_SPIN | LCB_F_WRITE , "rwlock:W" },
1508
+ { LCB_F_READ , "rwsem:R" },
1509
+ { LCB_F_WRITE , "rwsem:W" },
1510
+ { LCB_F_RT , "rtmutex" },
1511
+ { LCB_F_RT | LCB_F_READ , "rwlock-rt:R" },
1512
+ { LCB_F_RT | LCB_F_WRITE , "rwlock-rt:W" },
1513
+ { LCB_F_PERCPU | LCB_F_READ , "pcpu-sem:R" },
1514
+ { LCB_F_PERCPU | LCB_F_WRITE , "pcpu-sem:W" },
1515
+ { LCB_F_MUTEX , "mutex" },
1516
+ { LCB_F_MUTEX | LCB_F_SPIN , "mutex" },
1517
+ };
1518
+
1519
+ for (unsigned int i = 0 ; i < ARRAY_SIZE (table ); i ++ ) {
1520
+ if (table [i ].flags == st -> flags )
1521
+ return table [i ].name ;
1522
+ }
1523
+ return "unknown" ;
1524
+ }
1525
+
1526
+ static void sort_contention_result (void )
1527
+ {
1528
+ sort_result ();
1529
+ }
1530
+
1531
+ static void print_contention_result (void )
1532
+ {
1533
+ struct lock_stat * st ;
1534
+ struct lock_key * key ;
1535
+ int bad , total ;
1536
+
1537
+ list_for_each_entry (key , & lock_keys , list )
1538
+ pr_info ("%*s " , key -> len , key -> header );
1539
+
1540
+ pr_info (" %10s %s\n\n" , "type" , "caller" );
1541
+
1542
+ bad = total = 0 ;
1543
+ while ((st = pop_from_result ())) {
1544
+ total ++ ;
1545
+ if (st -> broken )
1546
+ bad ++ ;
1547
+
1548
+ list_for_each_entry (key , & lock_keys , list ) {
1549
+ key -> print (key , st );
1550
+ pr_info (" " );
1551
+ }
1552
+
1553
+ pr_info (" %10s %s\n" , get_type_str (st ), st -> name );
1554
+ }
1555
+
1556
+ print_bad_events (bad , total );
1557
+ }
1558
+
1431
1559
static const struct evsel_str_handler lock_tracepoints [] = {
1432
1560
{ "lock:lock_acquire" , evsel__process_lock_acquire , }, /* CONFIG_LOCKDEP */
1433
1561
{ "lock:lock_acquired" , evsel__process_lock_acquired , }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
@@ -1508,6 +1636,68 @@ static int __cmd_report(bool display_info)
1508
1636
return err ;
1509
1637
}
1510
1638
1639
+ static int __cmd_contention (void )
1640
+ {
1641
+ int err = - EINVAL ;
1642
+ struct perf_tool eops = {
1643
+ .sample = process_sample_event ,
1644
+ .comm = perf_event__process_comm ,
1645
+ .mmap = perf_event__process_mmap ,
1646
+ .ordered_events = true,
1647
+ };
1648
+ struct perf_data data = {
1649
+ .path = input_name ,
1650
+ .mode = PERF_DATA_MODE_READ ,
1651
+ .force = force ,
1652
+ };
1653
+
1654
+ session = perf_session__new (& data , & eops );
1655
+ if (IS_ERR (session )) {
1656
+ pr_err ("Initializing perf session failed\n" );
1657
+ return PTR_ERR (session );
1658
+ }
1659
+
1660
+ /* for lock function check */
1661
+ symbol_conf .sort_by_name = true;
1662
+ symbol__init (& session -> header .env );
1663
+
1664
+ if (!perf_session__has_traces (session , "lock record" ))
1665
+ goto out_delete ;
1666
+
1667
+ if (!evlist__find_evsel_by_str (session -> evlist , "lock:contention_begin" )) {
1668
+ pr_err ("lock contention evsel not found\n" );
1669
+ goto out_delete ;
1670
+ }
1671
+
1672
+ if (perf_session__set_tracepoints_handlers (session , contention_tracepoints )) {
1673
+ pr_err ("Initializing perf session tracepoint handlers failed\n" );
1674
+ goto out_delete ;
1675
+ }
1676
+
1677
+ if (setup_output_field ("contended,wait_total,wait_max,avg_wait" ))
1678
+ goto out_delete ;
1679
+
1680
+ sort_key = "wait_total" ;
1681
+ if (select_key ())
1682
+ goto out_delete ;
1683
+
1684
+ aggr_mode = LOCK_AGGR_CALLER ;
1685
+
1686
+ err = perf_session__process_events (session );
1687
+ if (err )
1688
+ goto out_delete ;
1689
+
1690
+ setup_pager ();
1691
+
1692
+ sort_contention_result ();
1693
+ print_contention_result ();
1694
+
1695
+ out_delete :
1696
+ perf_session__delete (session );
1697
+ return err ;
1698
+ }
1699
+
1700
+
1511
1701
static int __cmd_record (int argc , const char * * argv )
1512
1702
{
1513
1703
const char * record_args [] = {
@@ -1626,12 +1816,17 @@ int cmd_lock(int argc, const char **argv)
1626
1816
OPT_PARENT (lock_options )
1627
1817
};
1628
1818
1819
+ const struct option contention_options [] = {
1820
+ OPT_PARENT (lock_options )
1821
+ };
1822
+
1629
1823
const char * const info_usage [] = {
1630
1824
"perf lock info [<options>]" ,
1631
1825
NULL
1632
1826
};
1633
1827
const char * const lock_subcommands [] = { "record" , "report" , "script" ,
1634
- "info" , NULL };
1828
+ "info" , "contention" ,
1829
+ "contention" , NULL };
1635
1830
const char * lock_usage [] = {
1636
1831
NULL ,
1637
1832
NULL
@@ -1640,6 +1835,10 @@ int cmd_lock(int argc, const char **argv)
1640
1835
"perf lock report [<options>]" ,
1641
1836
NULL
1642
1837
};
1838
+ const char * const contention_usage [] = {
1839
+ "perf lock contention [<options>]" ,
1840
+ NULL
1841
+ };
1643
1842
unsigned int i ;
1644
1843
int rc = 0 ;
1645
1844
@@ -1675,6 +1874,17 @@ int cmd_lock(int argc, const char **argv)
1675
1874
/* recycling report_lock_ops */
1676
1875
trace_handler = & report_lock_ops ;
1677
1876
rc = __cmd_report (true);
1877
+ } else if (strlen (argv [0 ]) > 2 && strstarts ("contention" , argv [0 ])) {
1878
+ trace_handler = & contention_lock_ops ;
1879
+ if (argc ) {
1880
+ argc = parse_options (argc , argv , contention_options ,
1881
+ contention_usage , 0 );
1882
+ if (argc ) {
1883
+ usage_with_options (contention_usage ,
1884
+ contention_options );
1885
+ }
1886
+ }
1887
+ rc = __cmd_contention ();
1678
1888
} else {
1679
1889
usage_with_options (lock_usage , lock_options );
1680
1890
}
0 commit comments