9
9
#include "util/symbol.h"
10
10
#include "util/thread.h"
11
11
#include "util/header.h"
12
+ #include "util/callchain.h"
12
13
13
14
#include <subcmd/pager.h>
14
15
#include <subcmd/parse-options.h>
19
20
#include "util/tool.h"
20
21
#include "util/data.h"
21
22
#include "util/string2.h"
23
+ #include "util/map.h"
22
24
23
25
#include <sys/types.h>
24
26
#include <sys/prctl.h>
32
34
#include <linux/kernel.h>
33
35
#include <linux/zalloc.h>
34
36
#include <linux/err.h>
37
+ #include <linux/stringify.h>
35
38
36
39
static struct perf_session * session ;
37
40
@@ -120,6 +123,24 @@ static struct rb_root thread_stats;
120
123
static bool combine_locks ;
121
124
static bool show_thread_stats ;
122
125
126
+ /*
127
+ * CONTENTION_STACK_DEPTH
128
+ * Number of stack trace entries to find callers
129
+ */
130
+ #define CONTENTION_STACK_DEPTH 8
131
+
132
+ /*
133
+ * CONTENTION_STACK_SKIP
134
+ * Number of stack trace entries to skip when finding callers.
135
+ * The first few entries belong to the locking implementation itself.
136
+ */
137
+ #define CONTENTION_STACK_SKIP 3
138
+
139
+ static u64 sched_text_start ;
140
+ static u64 sched_text_end ;
141
+ static u64 lock_text_start ;
142
+ static u64 lock_text_end ;
143
+
123
144
static struct thread_stat * thread_stat_find (u32 tid )
124
145
{
125
146
struct rb_node * node ;
@@ -839,6 +860,116 @@ static int report_lock_release_event(struct evsel *evsel,
839
860
return 0 ;
840
861
}
841
862
863
+ static bool is_lock_function (u64 addr )
864
+ {
865
+ if (!sched_text_start ) {
866
+ struct machine * machine = & session -> machines .host ;
867
+ struct map * kmap ;
868
+ struct symbol * sym ;
869
+
870
+ sym = machine__find_kernel_symbol_by_name (machine ,
871
+ "__sched_text_start" ,
872
+ & kmap );
873
+ if (!sym ) {
874
+ /* to avoid retry */
875
+ sched_text_start = 1 ;
876
+ return false;
877
+ }
878
+
879
+ sched_text_start = kmap -> unmap_ip (kmap , sym -> start );
880
+
881
+ /* should not fail from here */
882
+ sym = machine__find_kernel_symbol_by_name (machine ,
883
+ "__sched_text_end" ,
884
+ & kmap );
885
+ sched_text_end = kmap -> unmap_ip (kmap , sym -> start );
886
+
887
+ sym = machine__find_kernel_symbol_by_name (machine ,
888
+ "__lock_text_start" ,
889
+ & kmap );
890
+ lock_text_start = kmap -> unmap_ip (kmap , sym -> start );
891
+
892
+ sym = machine__find_kernel_symbol_by_name (machine ,
893
+ "__lock_text_end" ,
894
+ & kmap );
895
+ lock_text_start = kmap -> unmap_ip (kmap , sym -> start );
896
+ }
897
+
898
+ /* failed to get kernel symbols */
899
+ if (sched_text_start == 1 )
900
+ return false;
901
+
902
+ /* mutex and rwsem functions are in sched text section */
903
+ if (sched_text_start <= addr && addr < sched_text_end )
904
+ return true;
905
+
906
+ /* spinlock functions are in lock text section */
907
+ if (lock_text_start <= addr && addr < lock_text_end )
908
+ return true;
909
+
910
+ return false;
911
+ }
912
+
913
+ static int lock_contention_caller (struct evsel * evsel , struct perf_sample * sample ,
914
+ char * buf , int size )
915
+ {
916
+ struct thread * thread ;
917
+ struct callchain_cursor * cursor = & callchain_cursor ;
918
+ struct symbol * sym ;
919
+ int skip = 0 ;
920
+ int ret ;
921
+
922
+ /* lock names will be replaced to task name later */
923
+ if (show_thread_stats )
924
+ return -1 ;
925
+
926
+ thread = machine__findnew_thread (& session -> machines .host ,
927
+ -1 , sample -> pid );
928
+ if (thread == NULL )
929
+ return -1 ;
930
+
931
+ /* use caller function name from the callchain */
932
+ ret = thread__resolve_callchain (thread , cursor , evsel , sample ,
933
+ NULL , NULL , CONTENTION_STACK_DEPTH );
934
+ if (ret != 0 ) {
935
+ thread__put (thread );
936
+ return -1 ;
937
+ }
938
+
939
+ callchain_cursor_commit (cursor );
940
+ thread__put (thread );
941
+
942
+ while (true) {
943
+ struct callchain_cursor_node * node ;
944
+
945
+ node = callchain_cursor_current (cursor );
946
+ if (node == NULL )
947
+ break ;
948
+
949
+ /* skip first few entries - for lock functions */
950
+ if (++ skip <= CONTENTION_STACK_SKIP )
951
+ goto next ;
952
+
953
+ sym = node -> ms .sym ;
954
+ if (sym && !is_lock_function (node -> ip )) {
955
+ struct map * map = node -> ms .map ;
956
+ u64 offset ;
957
+
958
+ offset = map -> map_ip (map , node -> ip ) - sym -> start ;
959
+
960
+ if (offset )
961
+ scnprintf (buf , size , "%s+%#lx" , sym -> name , offset );
962
+ else
963
+ strlcpy (buf , sym -> name , size );
964
+ return 0 ;
965
+ }
966
+
967
+ next :
968
+ callchain_cursor_advance (cursor );
969
+ }
970
+ return -1 ;
971
+ }
972
+
842
973
static int report_lock_contention_begin_event (struct evsel * evsel ,
843
974
struct perf_sample * sample )
844
975
{
@@ -850,9 +981,18 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
850
981
if (show_thread_stats )
851
982
addr = sample -> tid ;
852
983
853
- ls = lock_stat_findnew (addr , "No name" );
854
- if (!ls )
855
- return - ENOMEM ;
984
+ ls = lock_stat_find (addr );
985
+ if (!ls ) {
986
+ char buf [128 ];
987
+ const char * caller = buf ;
988
+
989
+ if (lock_contention_caller (evsel , sample , buf , sizeof (buf )) < 0 )
990
+ caller = "Unknown" ;
991
+
992
+ ls = lock_stat_findnew (addr , caller );
993
+ if (!ls )
994
+ return - ENOMEM ;
995
+ }
856
996
857
997
ts = thread_stat_findnew (sample -> tid );
858
998
if (!ts )
@@ -1233,6 +1373,7 @@ static int __cmd_report(bool display_info)
1233
1373
struct perf_tool eops = {
1234
1374
.sample = process_sample_event ,
1235
1375
.comm = perf_event__process_comm ,
1376
+ .mmap = perf_event__process_mmap ,
1236
1377
.namespaces = perf_event__process_namespaces ,
1237
1378
.ordered_events = true,
1238
1379
};
@@ -1248,6 +1389,8 @@ static int __cmd_report(bool display_info)
1248
1389
return PTR_ERR (session );
1249
1390
}
1250
1391
1392
+ /* for lock function check */
1393
+ symbol_conf .sort_by_name = true;
1251
1394
symbol__init (& session -> header .env );
1252
1395
1253
1396
if (!perf_session__has_traces (session , "lock record" ))
@@ -1292,8 +1435,12 @@ static int __cmd_record(int argc, const char **argv)
1292
1435
const char * record_args [] = {
1293
1436
"record" , "-R" , "-m" , "1024" , "-c" , "1" , "--synth" , "task" ,
1294
1437
};
1438
+ const char * callgraph_args [] = {
1439
+ "--call-graph" , "fp," __stringify (CONTENTION_STACK_DEPTH ),
1440
+ };
1295
1441
unsigned int rec_argc , i , j , ret ;
1296
1442
unsigned int nr_tracepoints ;
1443
+ unsigned int nr_callgraph_args = 0 ;
1297
1444
const char * * rec_argv ;
1298
1445
bool has_lock_stat = true;
1299
1446
@@ -1318,8 +1465,10 @@ static int __cmd_record(int argc, const char **argv)
1318
1465
}
1319
1466
}
1320
1467
1468
+ nr_callgraph_args = ARRAY_SIZE (callgraph_args );
1469
+
1321
1470
setup_args :
1322
- rec_argc = ARRAY_SIZE (record_args ) + argc - 1 ;
1471
+ rec_argc = ARRAY_SIZE (record_args ) + nr_callgraph_args + argc - 1 ;
1323
1472
1324
1473
if (has_lock_stat )
1325
1474
nr_tracepoints = ARRAY_SIZE (lock_tracepoints );
@@ -1351,6 +1500,9 @@ static int __cmd_record(int argc, const char **argv)
1351
1500
rec_argv [i ++ ] = ev_name ;
1352
1501
}
1353
1502
1503
+ for (j = 0 ; j < nr_callgraph_args ; j ++ , i ++ )
1504
+ rec_argv [i ] = callgraph_args [j ];
1505
+
1354
1506
for (j = 1 ; j < (unsigned int )argc ; j ++ , i ++ )
1355
1507
rec_argv [i ] = argv [j ];
1356
1508
0 commit comments