diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
| -rw-r--r-- | tools/perf/builtin-script.c | 121 |
1 files changed, 111 insertions, 10 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 22747de7234b..09024ec2ab2e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "util/util.h" | 13 | #include "util/util.h" |
| 14 | #include "util/evlist.h" | 14 | #include "util/evlist.h" |
| 15 | #include "util/evsel.h" | 15 | #include "util/evsel.h" |
| 16 | #include <linux/bitmap.h> | ||
| 16 | 17 | ||
| 17 | static char const *script_name; | 18 | static char const *script_name; |
| 18 | static char const *generate_script_lang; | 19 | static char const *generate_script_lang; |
| @@ -21,6 +22,8 @@ static u64 last_timestamp; | |||
| 21 | static u64 nr_unordered; | 22 | static u64 nr_unordered; |
| 22 | extern const struct option record_options[]; | 23 | extern const struct option record_options[]; |
| 23 | static bool no_callchain; | 24 | static bool no_callchain; |
| 25 | static const char *cpu_list; | ||
| 26 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
| 24 | 27 | ||
| 25 | enum perf_output_field { | 28 | enum perf_output_field { |
| 26 | PERF_OUTPUT_COMM = 1U << 0, | 29 | PERF_OUTPUT_COMM = 1U << 0, |
| @@ -30,7 +33,10 @@ enum perf_output_field { | |||
| 30 | PERF_OUTPUT_CPU = 1U << 4, | 33 | PERF_OUTPUT_CPU = 1U << 4, |
| 31 | PERF_OUTPUT_EVNAME = 1U << 5, | 34 | PERF_OUTPUT_EVNAME = 1U << 5, |
| 32 | PERF_OUTPUT_TRACE = 1U << 6, | 35 | PERF_OUTPUT_TRACE = 1U << 6, |
| 33 | PERF_OUTPUT_SYM = 1U << 7, | 36 | PERF_OUTPUT_IP = 1U << 7, |
| 37 | PERF_OUTPUT_SYM = 1U << 8, | ||
| 38 | PERF_OUTPUT_DSO = 1U << 9, | ||
| 39 | PERF_OUTPUT_ADDR = 1U << 10, | ||
| 34 | }; | 40 | }; |
| 35 | 41 | ||
| 36 | struct output_option { | 42 | struct output_option { |
| @@ -44,7 +50,10 @@ struct output_option { | |||
| 44 | {.str = "cpu", .field = PERF_OUTPUT_CPU}, | 50 | {.str = "cpu", .field = PERF_OUTPUT_CPU}, |
| 45 | {.str = "event", .field = PERF_OUTPUT_EVNAME}, | 51 | {.str = "event", .field = PERF_OUTPUT_EVNAME}, |
| 46 | {.str = "trace", .field = PERF_OUTPUT_TRACE}, | 52 | {.str = "trace", .field = PERF_OUTPUT_TRACE}, |
| 53 | {.str = "ip", .field = PERF_OUTPUT_IP}, | ||
| 47 | {.str = "sym", .field = PERF_OUTPUT_SYM}, | 54 | {.str = "sym", .field = PERF_OUTPUT_SYM}, |
| 55 | {.str = "dso", .field = PERF_OUTPUT_DSO}, | ||
| 56 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, | ||
| 48 | }; | 57 | }; |
| 49 | 58 | ||
| 50 | /* default set to maintain compatibility with current format */ | 59 | /* default set to maintain compatibility with current format */ |
| @@ -60,7 +69,8 @@ static struct { | |||
| 60 | 69 | ||
| 61 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 70 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
| 62 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 71 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
| 63 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 72 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
| 73 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
| 64 | 74 | ||
| 65 | .invalid_fields = PERF_OUTPUT_TRACE, | 75 | .invalid_fields = PERF_OUTPUT_TRACE, |
| 66 | }, | 76 | }, |
| @@ -70,7 +80,8 @@ static struct { | |||
| 70 | 80 | ||
| 71 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 81 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
| 72 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 82 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
| 73 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 83 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
| 84 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
| 74 | 85 | ||
| 75 | .invalid_fields = PERF_OUTPUT_TRACE, | 86 | .invalid_fields = PERF_OUTPUT_TRACE, |
| 76 | }, | 87 | }, |
| @@ -88,7 +99,8 @@ static struct { | |||
| 88 | 99 | ||
| 89 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 100 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
| 90 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 101 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
| 91 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 102 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
| 103 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
| 92 | 104 | ||
| 93 | .invalid_fields = PERF_OUTPUT_TRACE, | 105 | .invalid_fields = PERF_OUTPUT_TRACE, |
| 94 | }, | 106 | }, |
| @@ -157,9 +169,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
| 157 | !perf_session__has_traces(session, "record -R")) | 169 | !perf_session__has_traces(session, "record -R")) |
| 158 | return -EINVAL; | 170 | return -EINVAL; |
| 159 | 171 | ||
| 160 | if (PRINT_FIELD(SYM)) { | 172 | if (PRINT_FIELD(IP)) { |
| 161 | if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", | 173 | if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", |
| 162 | PERF_OUTPUT_SYM)) | 174 | PERF_OUTPUT_IP)) |
| 163 | return -EINVAL; | 175 | return -EINVAL; |
| 164 | 176 | ||
| 165 | if (!no_callchain && | 177 | if (!no_callchain && |
| @@ -167,6 +179,24 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
| 167 | symbol_conf.use_callchain = false; | 179 | symbol_conf.use_callchain = false; |
| 168 | } | 180 | } |
| 169 | 181 | ||
| 182 | if (PRINT_FIELD(ADDR) && | ||
| 183 | perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR", | ||
| 184 | PERF_OUTPUT_ADDR)) | ||
| 185 | return -EINVAL; | ||
| 186 | |||
| 187 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | ||
| 188 | pr_err("Display of symbols requested but neither sample IP nor " | ||
| 189 | "sample address\nis selected. Hence, no addresses to convert " | ||
| 190 | "to symbols.\n"); | ||
| 191 | return -EINVAL; | ||
| 192 | } | ||
| 193 | if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | ||
| 194 | pr_err("Display of DSO requested but neither sample IP nor " | ||
| 195 | "sample address\nis selected. Hence, no addresses to convert " | ||
| 196 | "to DSO.\n"); | ||
| 197 | return -EINVAL; | ||
| 198 | } | ||
| 199 | |||
| 170 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && | 200 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && |
| 171 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", | 201 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", |
| 172 | PERF_OUTPUT_TID|PERF_OUTPUT_PID)) | 202 | PERF_OUTPUT_TID|PERF_OUTPUT_PID)) |
| @@ -230,7 +260,7 @@ static void print_sample_start(struct perf_sample *sample, | |||
| 230 | if (PRINT_FIELD(COMM)) { | 260 | if (PRINT_FIELD(COMM)) { |
| 231 | if (latency_format) | 261 | if (latency_format) |
| 232 | printf("%8.8s ", thread->comm); | 262 | printf("%8.8s ", thread->comm); |
| 233 | else if (PRINT_FIELD(SYM) && symbol_conf.use_callchain) | 263 | else if (PRINT_FIELD(IP) && symbol_conf.use_callchain) |
| 234 | printf("%s ", thread->comm); | 264 | printf("%s ", thread->comm); |
| 235 | else | 265 | else |
| 236 | printf("%16s ", thread->comm); | 266 | printf("%16s ", thread->comm); |
| @@ -271,6 +301,63 @@ static void print_sample_start(struct perf_sample *sample, | |||
| 271 | } | 301 | } |
| 272 | } | 302 | } |
| 273 | 303 | ||
| 304 | static bool sample_addr_correlates_sym(struct perf_event_attr *attr) | ||
| 305 | { | ||
| 306 | if ((attr->type == PERF_TYPE_SOFTWARE) && | ||
| 307 | ((attr->config == PERF_COUNT_SW_PAGE_FAULTS) || | ||
| 308 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN) || | ||
| 309 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))) | ||
| 310 | return true; | ||
| 311 | |||
| 312 | return false; | ||
| 313 | } | ||
| 314 | |||
| 315 | static void print_sample_addr(union perf_event *event, | ||
| 316 | struct perf_sample *sample, | ||
| 317 | struct perf_session *session, | ||
| 318 | struct thread *thread, | ||
| 319 | struct perf_event_attr *attr) | ||
| 320 | { | ||
| 321 | struct addr_location al; | ||
| 322 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
| 323 | const char *symname, *dsoname; | ||
| 324 | |||
| 325 | printf("%16" PRIx64, sample->addr); | ||
| 326 | |||
| 327 | if (!sample_addr_correlates_sym(attr)) | ||
| 328 | return; | ||
| 329 | |||
| 330 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | ||
| 331 | event->ip.pid, sample->addr, &al); | ||
| 332 | if (!al.map) | ||
| 333 | thread__find_addr_map(thread, session, cpumode, MAP__VARIABLE, | ||
| 334 | event->ip.pid, sample->addr, &al); | ||
| 335 | |||
| 336 | al.cpu = sample->cpu; | ||
| 337 | al.sym = NULL; | ||
| 338 | |||
| 339 | if (al.map) | ||
| 340 | al.sym = map__find_symbol(al.map, al.addr, NULL); | ||
| 341 | |||
| 342 | if (PRINT_FIELD(SYM)) { | ||
| 343 | if (al.sym && al.sym->name) | ||
| 344 | symname = al.sym->name; | ||
| 345 | else | ||
| 346 | symname = ""; | ||
| 347 | |||
| 348 | printf(" %16s", symname); | ||
| 349 | } | ||
| 350 | |||
| 351 | if (PRINT_FIELD(DSO)) { | ||
| 352 | if (al.map && al.map->dso && al.map->dso->name) | ||
| 353 | dsoname = al.map->dso->name; | ||
| 354 | else | ||
| 355 | dsoname = ""; | ||
| 356 | |||
| 357 | printf(" (%s)", dsoname); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 274 | static void process_event(union perf_event *event __unused, | 361 | static void process_event(union perf_event *event __unused, |
| 275 | struct perf_sample *sample, | 362 | struct perf_sample *sample, |
| 276 | struct perf_evsel *evsel, | 363 | struct perf_evsel *evsel, |
| @@ -288,12 +375,16 @@ static void process_event(union perf_event *event __unused, | |||
| 288 | print_trace_event(sample->cpu, sample->raw_data, | 375 | print_trace_event(sample->cpu, sample->raw_data, |
| 289 | sample->raw_size); | 376 | sample->raw_size); |
| 290 | 377 | ||
| 291 | if (PRINT_FIELD(SYM)) { | 378 | if (PRINT_FIELD(ADDR)) |
| 379 | print_sample_addr(event, sample, session, thread, attr); | ||
| 380 | |||
| 381 | if (PRINT_FIELD(IP)) { | ||
| 292 | if (!symbol_conf.use_callchain) | 382 | if (!symbol_conf.use_callchain) |
| 293 | printf(" "); | 383 | printf(" "); |
| 294 | else | 384 | else |
| 295 | printf("\n"); | 385 | printf("\n"); |
| 296 | perf_session__print_symbols(event, sample, session); | 386 | perf_session__print_ip(event, sample, session, |
| 387 | PRINT_FIELD(SYM), PRINT_FIELD(DSO)); | ||
| 297 | } | 388 | } |
| 298 | 389 | ||
| 299 | printf("\n"); | 390 | printf("\n"); |
| @@ -365,6 +456,10 @@ static int process_sample_event(union perf_event *event, | |||
| 365 | last_timestamp = sample->time; | 456 | last_timestamp = sample->time; |
| 366 | return 0; | 457 | return 0; |
| 367 | } | 458 | } |
| 459 | |||
| 460 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
| 461 | return 0; | ||
| 462 | |||
| 368 | scripting_ops->process_event(event, sample, evsel, session, thread); | 463 | scripting_ops->process_event(event, sample, evsel, session, thread); |
| 369 | 464 | ||
| 370 | session->hists.stats.total_period += sample->period; | 465 | session->hists.stats.total_period += sample->period; |
| @@ -985,8 +1080,9 @@ static const struct option options[] = { | |||
| 985 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 1080 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
| 986 | "Look for files with symbols relative to this directory"), | 1081 | "Look for files with symbols relative to this directory"), |
| 987 | OPT_CALLBACK('f', "fields", NULL, "str", | 1082 | OPT_CALLBACK('f', "fields", NULL, "str", |
| 988 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,sym", | 1083 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", |
| 989 | parse_output_fields), | 1084 | parse_output_fields), |
| 1085 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
| 990 | 1086 | ||
| 991 | OPT_END() | 1087 | OPT_END() |
| 992 | }; | 1088 | }; |
| @@ -1167,6 +1263,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
| 1167 | if (session == NULL) | 1263 | if (session == NULL) |
| 1168 | return -ENOMEM; | 1264 | return -ENOMEM; |
| 1169 | 1265 | ||
| 1266 | if (cpu_list) { | ||
| 1267 | if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) | ||
| 1268 | return -1; | ||
| 1269 | } | ||
| 1270 | |||
| 1170 | if (!no_callchain) | 1271 | if (!no_callchain) |
| 1171 | symbol_conf.use_callchain = true; | 1272 | symbol_conf.use_callchain = true; |
| 1172 | else | 1273 | else |
