diff options
Diffstat (limited to 'tools')
36 files changed, 2783 insertions, 1179 deletions
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 6f5a498608b2..85c5f026930d 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
| @@ -66,6 +66,12 @@ OPTIONS | |||
| 66 | used. This interfaces starts by centering on the line with more | 66 | used. This interfaces starts by centering on the line with more |
| 67 | samples, TAB/UNTAB cycles through the lines with more samples. | 67 | samples, TAB/UNTAB cycles through the lines with more samples. |
| 68 | 68 | ||
| 69 | -c:: | ||
| 70 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | ||
| 71 | be provided as a comma-separated list with no space: 0,1. Ranges of | ||
| 72 | CPUs are specified with -: 0-2. Default is to report samples on all | ||
| 73 | CPUs. | ||
| 74 | |||
| 69 | SEE ALSO | 75 | SEE ALSO |
| 70 | -------- | 76 | -------- |
| 71 | linkperf:perf-record[1], linkperf:perf-report[1] | 77 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 02bafce4b341..2780d9ce48bf 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
| @@ -34,9 +34,11 @@ OPTIONS | |||
| 34 | Specify vmlinux path which has debuginfo (Dwarf binary). | 34 | Specify vmlinux path which has debuginfo (Dwarf binary). |
| 35 | 35 | ||
| 36 | -m:: | 36 | -m:: |
| 37 | --module=MODNAME:: | 37 | --module=MODNAME|PATH:: |
| 38 | Specify module name in which perf-probe searches probe points | 38 | Specify module name in which perf-probe searches probe points |
| 39 | or lines. | 39 | or lines. If a path of module file is passed, perf-probe |
| 40 | treat it as an offline module (this means you can add a probe on | ||
| 41 | a module which has not been loaded yet). | ||
| 40 | 42 | ||
| 41 | -s:: | 43 | -s:: |
| 42 | --source=PATH:: | 44 | --source=PATH:: |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 8ba03d6e5398..04253c07d19a 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -80,15 +80,24 @@ OPTIONS | |||
| 80 | --dump-raw-trace:: | 80 | --dump-raw-trace:: |
| 81 | Dump raw trace in ASCII. | 81 | Dump raw trace in ASCII. |
| 82 | 82 | ||
| 83 | -g [type,min]:: | 83 | -g [type,min,order]:: |
| 84 | --call-graph:: | 84 | --call-graph:: |
| 85 | Display call chains using type and min percent threshold. | 85 | Display call chains using type, min percent threshold and order. |
| 86 | type can be either: | 86 | type can be either: |
| 87 | - flat: single column, linear exposure of call chains. | 87 | - flat: single column, linear exposure of call chains. |
| 88 | - graph: use a graph tree, displaying absolute overhead rates. | 88 | - graph: use a graph tree, displaying absolute overhead rates. |
| 89 | - fractal: like graph, but displays relative rates. Each branch of | 89 | - fractal: like graph, but displays relative rates. Each branch of |
| 90 | the tree is considered as a new profiled object. + | 90 | the tree is considered as a new profiled object. + |
| 91 | Default: fractal,0.5. | 91 | |
| 92 | order can be either: | ||
| 93 | - callee: callee based call graph. | ||
| 94 | - caller: inverted caller based call graph. | ||
| 95 | |||
| 96 | Default: fractal,0.5,callee. | ||
| 97 | |||
| 98 | -G:: | ||
| 99 | --inverted:: | ||
| 100 | alias for inverted caller based call graph. | ||
| 92 | 101 | ||
| 93 | --pretty=<key>:: | 102 | --pretty=<key>:: |
| 94 | Pretty printing style. key: normal, raw | 103 | Pretty printing style. key: normal, raw |
| @@ -119,6 +128,12 @@ OPTIONS | |||
| 119 | --symfs=<directory>:: | 128 | --symfs=<directory>:: |
| 120 | Look for files with symbols relative to this directory. | 129 | Look for files with symbols relative to this directory. |
| 121 | 130 | ||
| 131 | -c:: | ||
| 132 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | ||
| 133 | be provided as a comma-separated list with no space: 0,1. Ranges of | ||
| 134 | CPUs are specified with -: 0-2. Default is to report samples on all | ||
| 135 | CPUs. | ||
| 136 | |||
| 122 | SEE ALSO | 137 | SEE ALSO |
| 123 | -------- | 138 | -------- |
| 124 | linkperf:perf-stat[1] | 139 | linkperf:perf-stat[1] |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 86c87e214b11..db017867d9e8 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
| @@ -115,10 +115,10 @@ OPTIONS | |||
| 115 | -f:: | 115 | -f:: |
| 116 | --fields:: | 116 | --fields:: |
| 117 | Comma separated list of fields to print. Options are: | 117 | Comma separated list of fields to print. Options are: |
| 118 | comm, tid, pid, time, cpu, event, trace, sym. Field | 118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr. |
| 119 | list can be prepended with the type, trace, sw or hw, | 119 | Field list can be prepended with the type, trace, sw or hw, |
| 120 | to indicate to which event type the field list applies. | 120 | to indicate to which event type the field list applies. |
| 121 | e.g., -f sw:comm,tid,time,sym and -f trace:time,cpu,trace | 121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace |
| 122 | 122 | ||
| 123 | perf script -f <fields> | 123 | perf script -f <fields> |
| 124 | 124 | ||
| @@ -132,17 +132,17 @@ OPTIONS | |||
| 132 | The arguments are processed in the order received. A later usage can | 132 | The arguments are processed in the order received. A later usage can |
| 133 | reset a prior request. e.g.: | 133 | reset a prior request. e.g.: |
| 134 | 134 | ||
| 135 | -f trace: -f comm,tid,time,sym | 135 | -f trace: -f comm,tid,time,ip,sym |
| 136 | 136 | ||
| 137 | The first -f suppresses trace events (field list is ""), but then the | 137 | The first -f suppresses trace events (field list is ""), but then the |
| 138 | second invocation sets the fields to comm,tid,time,sym. In this case a | 138 | second invocation sets the fields to comm,tid,time,ip,sym. In this case a |
| 139 | warning is given to the user: | 139 | warning is given to the user: |
| 140 | 140 | ||
| 141 | "Overriding previous field request for all events." | 141 | "Overriding previous field request for all events." |
| 142 | 142 | ||
| 143 | Alternativey, consider the order: | 143 | Alternativey, consider the order: |
| 144 | 144 | ||
| 145 | -f comm,tid,time,sym -f trace: | 145 | -f comm,tid,time,ip,sym -f trace: |
| 146 | 146 | ||
| 147 | The first -f sets the fields for all events and the second -f | 147 | The first -f sets the fields for all events and the second -f |
| 148 | suppresses trace events. The user is given a warning message about | 148 | suppresses trace events. The user is given a warning message about |
| @@ -182,6 +182,12 @@ OPTIONS | |||
| 182 | --hide-call-graph:: | 182 | --hide-call-graph:: |
| 183 | When printing symbols do not display call chain. | 183 | When printing symbols do not display call chain. |
| 184 | 184 | ||
| 185 | -c:: | ||
| 186 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | ||
| 187 | be provided as a comma-separated list with no space: 0,1. Ranges of | ||
| 188 | CPUs are specified with -: 0-2. Default is to report samples on all | ||
| 189 | CPUs. | ||
| 190 | |||
| 185 | SEE ALSO | 191 | SEE ALSO |
| 186 | -------- | 192 | -------- |
| 187 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 193 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 940257b5774e..56d62d3fb167 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -52,7 +52,10 @@ ifeq ($(ARCH),i386) | |||
| 52 | endif | 52 | endif |
| 53 | ifeq ($(ARCH),x86_64) | 53 | ifeq ($(ARCH),x86_64) |
| 54 | ARCH := x86 | 54 | ARCH := x86 |
| 55 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) | 55 | IS_X86_64 := 0 |
| 56 | ifeq (, $(findstring m32,$(EXTRA_CFLAGS))) | ||
| 57 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) | ||
| 58 | endif | ||
| 56 | ifeq (${IS_X86_64}, 1) | 59 | ifeq (${IS_X86_64}, 1) |
| 57 | RAW_ARCH := x86_64 | 60 | RAW_ARCH := x86_64 |
| 58 | ARCH_CFLAGS := -DARCH_X86_64 | 61 | ARCH_CFLAGS := -DARCH_X86_64 |
| @@ -279,6 +282,7 @@ LIB_H += util/thread.h | |||
| 279 | LIB_H += util/thread_map.h | 282 | LIB_H += util/thread_map.h |
| 280 | LIB_H += util/trace-event.h | 283 | LIB_H += util/trace-event.h |
| 281 | LIB_H += util/probe-finder.h | 284 | LIB_H += util/probe-finder.h |
| 285 | LIB_H += util/dwarf-aux.h | ||
| 282 | LIB_H += util/probe-event.h | 286 | LIB_H += util/probe-event.h |
| 283 | LIB_H += util/pstack.h | 287 | LIB_H += util/pstack.h |
| 284 | LIB_H += util/cpumap.h | 288 | LIB_H += util/cpumap.h |
| @@ -435,6 +439,7 @@ else | |||
| 435 | BASIC_CFLAGS += -DDWARF_SUPPORT | 439 | BASIC_CFLAGS += -DDWARF_SUPPORT |
| 436 | EXTLIBS += -lelf -ldw | 440 | EXTLIBS += -lelf -ldw |
| 437 | LIB_OBJS += $(OUTPUT)util/probe-finder.o | 441 | LIB_OBJS += $(OUTPUT)util/probe-finder.o |
| 442 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o | ||
| 438 | endif # PERF_HAVE_DWARF_REGS | 443 | endif # PERF_HAVE_DWARF_REGS |
| 439 | endif # NO_DWARF | 444 | endif # NO_DWARF |
| 440 | 445 | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 7b139e1e7e86..555aefd7fe01 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -28,6 +28,8 @@ | |||
| 28 | #include "util/hist.h" | 28 | #include "util/hist.h" |
| 29 | #include "util/session.h" | 29 | #include "util/session.h" |
| 30 | 30 | ||
| 31 | #include <linux/bitmap.h> | ||
| 32 | |||
| 31 | static char const *input_name = "perf.data"; | 33 | static char const *input_name = "perf.data"; |
| 32 | 34 | ||
| 33 | static bool force, use_tui, use_stdio; | 35 | static bool force, use_tui, use_stdio; |
| @@ -38,6 +40,9 @@ static bool print_line; | |||
| 38 | 40 | ||
| 39 | static const char *sym_hist_filter; | 41 | static const char *sym_hist_filter; |
| 40 | 42 | ||
| 43 | static const char *cpu_list; | ||
| 44 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
| 45 | |||
| 41 | static int perf_evlist__add_sample(struct perf_evlist *evlist, | 46 | static int perf_evlist__add_sample(struct perf_evlist *evlist, |
| 42 | struct perf_sample *sample, | 47 | struct perf_sample *sample, |
| 43 | struct perf_evsel *evsel, | 48 | struct perf_evsel *evsel, |
| @@ -90,6 +95,9 @@ static int process_sample_event(union perf_event *event, | |||
| 90 | return -1; | 95 | return -1; |
| 91 | } | 96 | } |
| 92 | 97 | ||
| 98 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
| 99 | return 0; | ||
| 100 | |||
| 93 | if (!al.filtered && | 101 | if (!al.filtered && |
| 94 | perf_evlist__add_sample(session->evlist, sample, evsel, &al)) { | 102 | perf_evlist__add_sample(session->evlist, sample, evsel, &al)) { |
| 95 | pr_warning("problem incrementing symbol count, " | 103 | pr_warning("problem incrementing symbol count, " |
| @@ -177,6 +185,12 @@ static int __cmd_annotate(void) | |||
| 177 | if (session == NULL) | 185 | if (session == NULL) |
| 178 | return -ENOMEM; | 186 | return -ENOMEM; |
| 179 | 187 | ||
| 188 | if (cpu_list) { | ||
| 189 | ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); | ||
| 190 | if (ret) | ||
| 191 | goto out_delete; | ||
| 192 | } | ||
| 193 | |||
| 180 | ret = perf_session__process_events(session, &event_ops); | 194 | ret = perf_session__process_events(session, &event_ops); |
| 181 | if (ret) | 195 | if (ret) |
| 182 | goto out_delete; | 196 | goto out_delete; |
| @@ -252,6 +266,7 @@ static const struct option options[] = { | |||
| 252 | "print matching source lines (may be slow)"), | 266 | "print matching source lines (may be slow)"), |
| 253 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 267 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
| 254 | "Don't shorten the displayed pathnames"), | 268 | "Don't shorten the displayed pathnames"), |
| 269 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
| 255 | OPT_END() | 270 | OPT_END() |
| 256 | }; | 271 | }; |
| 257 | 272 | ||
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 2c0e64d0b4aa..5f2a5c7046df 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
| @@ -242,7 +242,8 @@ static const struct option options[] = { | |||
| 242 | OPT_STRING('s', "source", &symbol_conf.source_prefix, | 242 | OPT_STRING('s', "source", &symbol_conf.source_prefix, |
| 243 | "directory", "path to kernel source"), | 243 | "directory", "path to kernel source"), |
| 244 | OPT_STRING('m', "module", ¶ms.target_module, | 244 | OPT_STRING('m', "module", ¶ms.target_module, |
| 245 | "modname", "target module name"), | 245 | "modname|path", |
| 246 | "target module name (for online) or path (for offline)"), | ||
| 246 | #endif | 247 | #endif |
| 247 | OPT__DRY_RUN(&probe_event_dry_run), | 248 | OPT__DRY_RUN(&probe_event_dry_run), |
| 248 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 249 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8e2c85798185..80dc5b790e47 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -740,7 +740,7 @@ static bool force, append_file; | |||
| 740 | const struct option record_options[] = { | 740 | const struct option record_options[] = { |
| 741 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 741 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
| 742 | "event selector. use 'perf list' to list available events", | 742 | "event selector. use 'perf list' to list available events", |
| 743 | parse_events), | 743 | parse_events_option), |
| 744 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | 744 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
| 745 | "event filter", parse_filter), | 745 | "event filter", parse_filter), |
| 746 | OPT_INTEGER('p', "pid", &target_pid, | 746 | OPT_INTEGER('p', "pid", &target_pid, |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 287a173523a7..f854efda7686 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | #include "util/sort.h" | 33 | #include "util/sort.h" |
| 34 | #include "util/hist.h" | 34 | #include "util/hist.h" |
| 35 | 35 | ||
| 36 | #include <linux/bitmap.h> | ||
| 37 | |||
| 36 | static char const *input_name = "perf.data"; | 38 | static char const *input_name = "perf.data"; |
| 37 | 39 | ||
| 38 | static bool force, use_tui, use_stdio; | 40 | static bool force, use_tui, use_stdio; |
| @@ -45,9 +47,13 @@ static struct perf_read_values show_threads_values; | |||
| 45 | static const char default_pretty_printing_style[] = "normal"; | 47 | static const char default_pretty_printing_style[] = "normal"; |
| 46 | static const char *pretty_printing_style = default_pretty_printing_style; | 48 | static const char *pretty_printing_style = default_pretty_printing_style; |
| 47 | 49 | ||
| 48 | static char callchain_default_opt[] = "fractal,0.5"; | 50 | static char callchain_default_opt[] = "fractal,0.5,callee"; |
| 51 | static bool inverted_callchain; | ||
| 49 | static symbol_filter_t annotate_init; | 52 | static symbol_filter_t annotate_init; |
| 50 | 53 | ||
| 54 | static const char *cpu_list; | ||
| 55 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
| 56 | |||
| 51 | static int perf_session__add_hist_entry(struct perf_session *session, | 57 | static int perf_session__add_hist_entry(struct perf_session *session, |
| 52 | struct addr_location *al, | 58 | struct addr_location *al, |
| 53 | struct perf_sample *sample, | 59 | struct perf_sample *sample, |
| @@ -116,6 +122,9 @@ static int process_sample_event(union perf_event *event, | |||
| 116 | if (al.filtered || (hide_unresolved && al.sym == NULL)) | 122 | if (al.filtered || (hide_unresolved && al.sym == NULL)) |
| 117 | return 0; | 123 | return 0; |
| 118 | 124 | ||
| 125 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
| 126 | return 0; | ||
| 127 | |||
| 119 | if (al.map != NULL) | 128 | if (al.map != NULL) |
| 120 | al.map->dso->hit = 1; | 129 | al.map->dso->hit = 1; |
| 121 | 130 | ||
| @@ -262,6 +271,12 @@ static int __cmd_report(void) | |||
| 262 | if (session == NULL) | 271 | if (session == NULL) |
| 263 | return -ENOMEM; | 272 | return -ENOMEM; |
| 264 | 273 | ||
| 274 | if (cpu_list) { | ||
| 275 | ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); | ||
| 276 | if (ret) | ||
| 277 | goto out_delete; | ||
| 278 | } | ||
| 279 | |||
| 265 | if (show_threads) | 280 | if (show_threads) |
| 266 | perf_read_values_init(&show_threads_values); | 281 | perf_read_values_init(&show_threads_values); |
| 267 | 282 | ||
| @@ -386,13 +401,29 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 386 | if (!tok) | 401 | if (!tok) |
| 387 | goto setup; | 402 | goto setup; |
| 388 | 403 | ||
| 389 | tok2 = strtok(NULL, ","); | ||
| 390 | callchain_param.min_percent = strtod(tok, &endptr); | 404 | callchain_param.min_percent = strtod(tok, &endptr); |
| 391 | if (tok == endptr) | 405 | if (tok == endptr) |
| 392 | return -1; | 406 | return -1; |
| 393 | 407 | ||
| 394 | if (tok2) | 408 | /* get the print limit */ |
| 409 | tok2 = strtok(NULL, ","); | ||
| 410 | if (!tok2) | ||
| 411 | goto setup; | ||
| 412 | |||
| 413 | if (tok2[0] != 'c') { | ||
| 395 | callchain_param.print_limit = strtod(tok2, &endptr); | 414 | callchain_param.print_limit = strtod(tok2, &endptr); |
| 415 | tok2 = strtok(NULL, ","); | ||
| 416 | if (!tok2) | ||
| 417 | goto setup; | ||
| 418 | } | ||
| 419 | |||
| 420 | /* get the call chain order */ | ||
| 421 | if (!strcmp(tok2, "caller")) | ||
| 422 | callchain_param.order = ORDER_CALLER; | ||
| 423 | else if (!strcmp(tok2, "callee")) | ||
| 424 | callchain_param.order = ORDER_CALLEE; | ||
| 425 | else | ||
| 426 | return -1; | ||
| 396 | setup: | 427 | setup: |
| 397 | if (callchain_register_param(&callchain_param) < 0) { | 428 | if (callchain_register_param(&callchain_param) < 0) { |
| 398 | fprintf(stderr, "Can't register callchain params\n"); | 429 | fprintf(stderr, "Can't register callchain params\n"); |
| @@ -436,9 +467,10 @@ static const struct option options[] = { | |||
| 436 | "regex filter to identify parent, see: '--sort parent'"), | 467 | "regex filter to identify parent, see: '--sort parent'"), |
| 437 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, | 468 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
| 438 | "Only display entries with parent-match"), | 469 | "Only display entries with parent-match"), |
| 439 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", | 470 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order", |
| 440 | "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. " | 471 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. " |
| 441 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), | 472 | "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt), |
| 473 | OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"), | ||
| 442 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 474 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
| 443 | "only consider symbols in these dsos"), | 475 | "only consider symbols in these dsos"), |
| 444 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 476 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
| @@ -455,6 +487,7 @@ static const struct option options[] = { | |||
| 455 | "Only display entries resolved to a symbol"), | 487 | "Only display entries resolved to a symbol"), |
| 456 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 488 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
| 457 | "Look for files with symbols relative to this directory"), | 489 | "Look for files with symbols relative to this directory"), |
| 490 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
| 458 | OPT_END() | 491 | OPT_END() |
| 459 | }; | 492 | }; |
| 460 | 493 | ||
| @@ -467,6 +500,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 467 | else if (use_tui) | 500 | else if (use_tui) |
| 468 | use_browser = 1; | 501 | use_browser = 1; |
| 469 | 502 | ||
| 503 | if (inverted_callchain) | ||
| 504 | callchain_param.order = ORDER_CALLER; | ||
| 505 | |||
| 470 | if (strcmp(input_name, "-") != 0) | 506 | if (strcmp(input_name, "-") != 0) |
| 471 | setup_browser(true); | 507 | setup_browser(true); |
| 472 | else | 508 | else |
| @@ -504,7 +540,14 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 504 | if (parent_pattern != default_parent_pattern) { | 540 | if (parent_pattern != default_parent_pattern) { |
| 505 | if (sort_dimension__add("parent") < 0) | 541 | if (sort_dimension__add("parent") < 0) |
| 506 | return -1; | 542 | return -1; |
| 507 | sort_parent.elide = 1; | 543 | |
| 544 | /* | ||
| 545 | * Only show the parent fields if we explicitly | ||
| 546 | * sort that way. If we only use parent machinery | ||
| 547 | * for filtering, we don't want it. | ||
| 548 | */ | ||
| 549 | if (!strstr(sort_order, "parent")) | ||
| 550 | sort_parent.elide = 1; | ||
| 508 | } else | 551 | } else |
| 509 | symbol_conf.exclude_other = false; | 552 | symbol_conf.exclude_other = false; |
| 510 | 553 | ||
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 |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a9f06715e44d..1ad04ce29c34 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -61,6 +61,8 @@ | |||
| 61 | #include <locale.h> | 61 | #include <locale.h> |
| 62 | 62 | ||
| 63 | #define DEFAULT_SEPARATOR " " | 63 | #define DEFAULT_SEPARATOR " " |
| 64 | #define CNTR_NOT_SUPPORTED "<not supported>" | ||
| 65 | #define CNTR_NOT_COUNTED "<not counted>" | ||
| 64 | 66 | ||
| 65 | static struct perf_event_attr default_attrs[] = { | 67 | static struct perf_event_attr default_attrs[] = { |
| 66 | 68 | ||
| @@ -448,6 +450,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
| 448 | if (verbose) | 450 | if (verbose) |
| 449 | ui__warning("%s event is not supported by the kernel.\n", | 451 | ui__warning("%s event is not supported by the kernel.\n", |
| 450 | event_name(counter)); | 452 | event_name(counter)); |
| 453 | counter->supported = false; | ||
| 451 | continue; | 454 | continue; |
| 452 | } | 455 | } |
| 453 | 456 | ||
| @@ -466,6 +469,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
| 466 | die("Not all events could be opened.\n"); | 469 | die("Not all events could be opened.\n"); |
| 467 | return -1; | 470 | return -1; |
| 468 | } | 471 | } |
| 472 | counter->supported = true; | ||
| 469 | } | 473 | } |
| 470 | 474 | ||
| 471 | if (perf_evlist__set_filters(evsel_list)) { | 475 | if (perf_evlist__set_filters(evsel_list)) { |
| @@ -513,7 +517,10 @@ static void print_noise_pct(double total, double avg) | |||
| 513 | if (avg) | 517 | if (avg) |
| 514 | pct = 100.0*total/avg; | 518 | pct = 100.0*total/avg; |
| 515 | 519 | ||
| 516 | fprintf(stderr, " ( +-%6.2f%% )", pct); | 520 | if (csv_output) |
| 521 | fprintf(stderr, "%s%.2f%%", csv_sep, pct); | ||
| 522 | else | ||
| 523 | fprintf(stderr, " ( +-%6.2f%% )", pct); | ||
| 517 | } | 524 | } |
| 518 | 525 | ||
| 519 | static void print_noise(struct perf_evsel *evsel, double avg) | 526 | static void print_noise(struct perf_evsel *evsel, double avg) |
| @@ -861,7 +868,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
| 861 | if (scaled == -1) { | 868 | if (scaled == -1) { |
| 862 | fprintf(stderr, "%*s%s%*s", | 869 | fprintf(stderr, "%*s%s%*s", |
| 863 | csv_output ? 0 : 18, | 870 | csv_output ? 0 : 18, |
| 864 | "<not counted>", | 871 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
| 865 | csv_sep, | 872 | csv_sep, |
| 866 | csv_output ? 0 : -24, | 873 | csv_output ? 0 : -24, |
| 867 | event_name(counter)); | 874 | event_name(counter)); |
| @@ -878,13 +885,13 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
| 878 | else | 885 | else |
| 879 | abs_printout(-1, counter, avg); | 886 | abs_printout(-1, counter, avg); |
| 880 | 887 | ||
| 888 | print_noise(counter, avg); | ||
| 889 | |||
| 881 | if (csv_output) { | 890 | if (csv_output) { |
| 882 | fputc('\n', stderr); | 891 | fputc('\n', stderr); |
| 883 | return; | 892 | return; |
| 884 | } | 893 | } |
| 885 | 894 | ||
| 886 | print_noise(counter, avg); | ||
| 887 | |||
| 888 | if (scaled) { | 895 | if (scaled) { |
| 889 | double avg_enabled, avg_running; | 896 | double avg_enabled, avg_running; |
| 890 | 897 | ||
| @@ -914,7 +921,8 @@ static void print_counter(struct perf_evsel *counter) | |||
| 914 | csv_output ? 0 : -4, | 921 | csv_output ? 0 : -4, |
| 915 | evsel_list->cpus->map[cpu], csv_sep, | 922 | evsel_list->cpus->map[cpu], csv_sep, |
| 916 | csv_output ? 0 : 18, | 923 | csv_output ? 0 : 18, |
| 917 | "<not counted>", csv_sep, | 924 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
| 925 | csv_sep, | ||
| 918 | csv_output ? 0 : -24, | 926 | csv_output ? 0 : -24, |
| 919 | event_name(counter)); | 927 | event_name(counter)); |
| 920 | 928 | ||
| @@ -1024,7 +1032,7 @@ static int stat__set_big_num(const struct option *opt __used, | |||
| 1024 | static const struct option options[] = { | 1032 | static const struct option options[] = { |
| 1025 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 1033 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
| 1026 | "event selector. use 'perf list' to list available events", | 1034 | "event selector. use 'perf list' to list available events", |
| 1027 | parse_events), | 1035 | parse_events_option), |
| 1028 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | 1036 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
| 1029 | "event filter", parse_filter), | 1037 | "event filter", parse_filter), |
| 1030 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 1038 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 2da9162262b0..55f4c76f2821 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "util/parse-events.h" | 12 | #include "util/parse-events.h" |
| 13 | #include "util/symbol.h" | 13 | #include "util/symbol.h" |
| 14 | #include "util/thread_map.h" | 14 | #include "util/thread_map.h" |
| 15 | #include "../../include/linux/hw_breakpoint.h" | ||
| 15 | 16 | ||
| 16 | static long page_size; | 17 | static long page_size; |
| 17 | 18 | ||
| @@ -245,8 +246,8 @@ static int trace_event__id(const char *evname) | |||
| 245 | int err = -1, fd; | 246 | int err = -1, fd; |
| 246 | 247 | ||
| 247 | if (asprintf(&filename, | 248 | if (asprintf(&filename, |
| 248 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", | 249 | "%s/syscalls/%s/id", |
| 249 | evname) < 0) | 250 | debugfs_path, evname) < 0) |
| 250 | return -1; | 251 | return -1; |
| 251 | 252 | ||
| 252 | fd = open(filename, O_RDONLY); | 253 | fd = open(filename, O_RDONLY); |
| @@ -600,6 +601,246 @@ out_free_threads: | |||
| 600 | #undef nsyscalls | 601 | #undef nsyscalls |
| 601 | } | 602 | } |
| 602 | 603 | ||
| 604 | #define TEST_ASSERT_VAL(text, cond) \ | ||
| 605 | do { \ | ||
| 606 | if (!cond) { \ | ||
| 607 | pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ | ||
| 608 | return -1; \ | ||
| 609 | } \ | ||
| 610 | } while (0) | ||
| 611 | |||
| 612 | static int test__checkevent_tracepoint(struct perf_evlist *evlist) | ||
| 613 | { | ||
| 614 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 615 | struct perf_evsel, node); | ||
| 616 | |||
| 617 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 618 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
| 619 | TEST_ASSERT_VAL("wrong sample_type", | ||
| 620 | (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == | ||
| 621 | evsel->attr.sample_type); | ||
| 622 | TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); | ||
| 623 | return 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) | ||
| 627 | { | ||
| 628 | struct perf_evsel *evsel; | ||
| 629 | |||
| 630 | TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); | ||
| 631 | |||
| 632 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 633 | TEST_ASSERT_VAL("wrong type", | ||
| 634 | PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
| 635 | TEST_ASSERT_VAL("wrong sample_type", | ||
| 636 | (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) | ||
| 637 | == evsel->attr.sample_type); | ||
| 638 | TEST_ASSERT_VAL("wrong sample_period", | ||
| 639 | 1 == evsel->attr.sample_period); | ||
| 640 | } | ||
| 641 | return 0; | ||
| 642 | } | ||
| 643 | |||
| 644 | static int test__checkevent_raw(struct perf_evlist *evlist) | ||
| 645 | { | ||
| 646 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 647 | struct perf_evsel, node); | ||
| 648 | |||
| 649 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 650 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
| 651 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
| 652 | return 0; | ||
| 653 | } | ||
| 654 | |||
| 655 | static int test__checkevent_numeric(struct perf_evlist *evlist) | ||
| 656 | { | ||
| 657 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 658 | struct perf_evsel, node); | ||
| 659 | |||
| 660 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 661 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | ||
| 662 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
| 663 | return 0; | ||
| 664 | } | ||
| 665 | |||
| 666 | static int test__checkevent_symbolic_name(struct perf_evlist *evlist) | ||
| 667 | { | ||
| 668 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 669 | struct perf_evsel, node); | ||
| 670 | |||
| 671 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 672 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
| 673 | TEST_ASSERT_VAL("wrong config", | ||
| 674 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
| 675 | return 0; | ||
| 676 | } | ||
| 677 | |||
| 678 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) | ||
| 679 | { | ||
| 680 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 681 | struct perf_evsel, node); | ||
| 682 | |||
| 683 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 684 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); | ||
| 685 | TEST_ASSERT_VAL("wrong config", | ||
| 686 | PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config); | ||
| 687 | return 0; | ||
| 688 | } | ||
| 689 | |||
| 690 | static int test__checkevent_genhw(struct perf_evlist *evlist) | ||
| 691 | { | ||
| 692 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 693 | struct perf_evsel, node); | ||
| 694 | |||
| 695 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 696 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type); | ||
| 697 | TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config); | ||
| 698 | return 0; | ||
| 699 | } | ||
| 700 | |||
| 701 | static int test__checkevent_breakpoint(struct perf_evlist *evlist) | ||
| 702 | { | ||
| 703 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 704 | struct perf_evsel, node); | ||
| 705 | |||
| 706 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 707 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 708 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 709 | TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == | ||
| 710 | evsel->attr.bp_type); | ||
| 711 | TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 == | ||
| 712 | evsel->attr.bp_len); | ||
| 713 | return 0; | ||
| 714 | } | ||
| 715 | |||
| 716 | static int test__checkevent_breakpoint_x(struct perf_evlist *evlist) | ||
| 717 | { | ||
| 718 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 719 | struct perf_evsel, node); | ||
| 720 | |||
| 721 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 722 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 723 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 724 | TEST_ASSERT_VAL("wrong bp_type", | ||
| 725 | HW_BREAKPOINT_X == evsel->attr.bp_type); | ||
| 726 | TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len); | ||
| 727 | return 0; | ||
| 728 | } | ||
| 729 | |||
| 730 | static int test__checkevent_breakpoint_r(struct perf_evlist *evlist) | ||
| 731 | { | ||
| 732 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 733 | struct perf_evsel, node); | ||
| 734 | |||
| 735 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 736 | TEST_ASSERT_VAL("wrong type", | ||
| 737 | PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 738 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 739 | TEST_ASSERT_VAL("wrong bp_type", | ||
| 740 | HW_BREAKPOINT_R == evsel->attr.bp_type); | ||
| 741 | TEST_ASSERT_VAL("wrong bp_len", | ||
| 742 | HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); | ||
| 743 | return 0; | ||
| 744 | } | ||
| 745 | |||
| 746 | static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) | ||
| 747 | { | ||
| 748 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
| 749 | struct perf_evsel, node); | ||
| 750 | |||
| 751 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 752 | TEST_ASSERT_VAL("wrong type", | ||
| 753 | PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 754 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 755 | TEST_ASSERT_VAL("wrong bp_type", | ||
| 756 | HW_BREAKPOINT_W == evsel->attr.bp_type); | ||
| 757 | TEST_ASSERT_VAL("wrong bp_len", | ||
| 758 | HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); | ||
| 759 | return 0; | ||
| 760 | } | ||
| 761 | |||
| 762 | static struct test__event_st { | ||
| 763 | const char *name; | ||
| 764 | __u32 type; | ||
| 765 | int (*check)(struct perf_evlist *evlist); | ||
| 766 | } test__events[] = { | ||
| 767 | { | ||
| 768 | .name = "syscalls:sys_enter_open", | ||
| 769 | .check = test__checkevent_tracepoint, | ||
| 770 | }, | ||
| 771 | { | ||
| 772 | .name = "syscalls:*", | ||
| 773 | .check = test__checkevent_tracepoint_multi, | ||
| 774 | }, | ||
| 775 | { | ||
| 776 | .name = "r1", | ||
| 777 | .check = test__checkevent_raw, | ||
| 778 | }, | ||
| 779 | { | ||
| 780 | .name = "1:1", | ||
| 781 | .check = test__checkevent_numeric, | ||
| 782 | }, | ||
| 783 | { | ||
| 784 | .name = "instructions", | ||
| 785 | .check = test__checkevent_symbolic_name, | ||
| 786 | }, | ||
| 787 | { | ||
| 788 | .name = "faults", | ||
| 789 | .check = test__checkevent_symbolic_alias, | ||
| 790 | }, | ||
| 791 | { | ||
| 792 | .name = "L1-dcache-load-miss", | ||
| 793 | .check = test__checkevent_genhw, | ||
| 794 | }, | ||
| 795 | { | ||
| 796 | .name = "mem:0", | ||
| 797 | .check = test__checkevent_breakpoint, | ||
| 798 | }, | ||
| 799 | { | ||
| 800 | .name = "mem:0:x", | ||
| 801 | .check = test__checkevent_breakpoint_x, | ||
| 802 | }, | ||
| 803 | { | ||
| 804 | .name = "mem:0:r", | ||
| 805 | .check = test__checkevent_breakpoint_r, | ||
| 806 | }, | ||
| 807 | { | ||
| 808 | .name = "mem:0:w", | ||
| 809 | .check = test__checkevent_breakpoint_w, | ||
| 810 | }, | ||
| 811 | }; | ||
| 812 | |||
| 813 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) | ||
| 814 | |||
| 815 | static int test__parse_events(void) | ||
| 816 | { | ||
| 817 | struct perf_evlist *evlist; | ||
| 818 | u_int i; | ||
| 819 | int ret = 0; | ||
| 820 | |||
| 821 | for (i = 0; i < TEST__EVENTS_CNT; i++) { | ||
| 822 | struct test__event_st *e = &test__events[i]; | ||
| 823 | |||
| 824 | evlist = perf_evlist__new(NULL, NULL); | ||
| 825 | if (evlist == NULL) | ||
| 826 | break; | ||
| 827 | |||
| 828 | ret = parse_events(evlist, e->name, 0); | ||
| 829 | if (ret) { | ||
| 830 | pr_debug("failed to parse event '%s', err %d\n", | ||
| 831 | e->name, ret); | ||
| 832 | break; | ||
| 833 | } | ||
| 834 | |||
| 835 | ret = e->check(evlist); | ||
| 836 | if (ret) | ||
| 837 | break; | ||
| 838 | |||
| 839 | perf_evlist__delete(evlist); | ||
| 840 | } | ||
| 841 | |||
| 842 | return ret; | ||
| 843 | } | ||
| 603 | static struct test { | 844 | static struct test { |
| 604 | const char *desc; | 845 | const char *desc; |
| 605 | int (*func)(void); | 846 | int (*func)(void); |
| @@ -621,6 +862,10 @@ static struct test { | |||
| 621 | .func = test__basic_mmap, | 862 | .func = test__basic_mmap, |
| 622 | }, | 863 | }, |
| 623 | { | 864 | { |
| 865 | .desc = "parse events tests", | ||
| 866 | .func = test__parse_events, | ||
| 867 | }, | ||
| 868 | { | ||
| 624 | .func = NULL, | 869 | .func = NULL, |
| 625 | }, | 870 | }, |
| 626 | }; | 871 | }; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index f2f3f4937aa2..a43433f08300 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -990,7 +990,7 @@ static const char * const top_usage[] = { | |||
| 990 | static const struct option options[] = { | 990 | static const struct option options[] = { |
| 991 | OPT_CALLBACK('e', "event", &top.evlist, "event", | 991 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
| 992 | "event selector. use 'perf list' to list available events", | 992 | "event selector. use 'perf list' to list available events", |
| 993 | parse_events), | 993 | parse_events_option), |
| 994 | OPT_INTEGER('c', "count", &default_interval, | 994 | OPT_INTEGER('c', "count", &default_interval, |
| 995 | "event period to sample"), | 995 | "event period to sample"), |
| 996 | OPT_INTEGER('p', "pid", &top.target_pid, | 996 | OPT_INTEGER('p', "pid", &top.target_pid, |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 1a79df9f739f..9b4ff16cac96 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -14,6 +14,11 @@ enum chain_mode { | |||
| 14 | CHAIN_GRAPH_REL | 14 | CHAIN_GRAPH_REL |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | enum chain_order { | ||
| 18 | ORDER_CALLER, | ||
| 19 | ORDER_CALLEE | ||
| 20 | }; | ||
| 21 | |||
| 17 | struct callchain_node { | 22 | struct callchain_node { |
| 18 | struct callchain_node *parent; | 23 | struct callchain_node *parent; |
| 19 | struct list_head siblings; | 24 | struct list_head siblings; |
| @@ -41,6 +46,7 @@ struct callchain_param { | |||
| 41 | u32 print_limit; | 46 | u32 print_limit; |
| 42 | double min_percent; | 47 | double min_percent; |
| 43 | sort_chain_func_t sort; | 48 | sort_chain_func_t sort; |
| 49 | enum chain_order order; | ||
| 44 | }; | 50 | }; |
| 45 | 51 | ||
| 46 | struct callchain_list { | 52 | struct callchain_list { |
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c new file mode 100644 index 000000000000..fddf40f30d3e --- /dev/null +++ b/tools/perf/util/dwarf-aux.c | |||
| @@ -0,0 +1,663 @@ | |||
| 1 | /* | ||
| 2 | * dwarf-aux.c : libdw auxiliary interfaces | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation; either version 2 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program; if not, write to the Free Software | ||
| 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <stdbool.h> | ||
| 21 | #include "util.h" | ||
| 22 | #include "debug.h" | ||
| 23 | #include "dwarf-aux.h" | ||
| 24 | |||
| 25 | /** | ||
| 26 | * cu_find_realpath - Find the realpath of the target file | ||
| 27 | * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit) | ||
| 28 | * @fname: The tail filename of the target file | ||
| 29 | * | ||
| 30 | * Find the real(long) path of @fname in @cu_die. | ||
| 31 | */ | ||
| 32 | const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
| 33 | { | ||
| 34 | Dwarf_Files *files; | ||
| 35 | size_t nfiles, i; | ||
| 36 | const char *src = NULL; | ||
| 37 | int ret; | ||
| 38 | |||
| 39 | if (!fname) | ||
| 40 | return NULL; | ||
| 41 | |||
| 42 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | ||
| 43 | if (ret != 0) | ||
| 44 | return NULL; | ||
| 45 | |||
| 46 | for (i = 0; i < nfiles; i++) { | ||
| 47 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
| 48 | if (strtailcmp(src, fname) == 0) | ||
| 49 | break; | ||
| 50 | } | ||
| 51 | if (i == nfiles) | ||
| 52 | return NULL; | ||
| 53 | return src; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * cu_get_comp_dir - Get the path of compilation directory | ||
| 58 | * @cu_die: a CU DIE | ||
| 59 | * | ||
| 60 | * Get the path of compilation directory of given @cu_die. | ||
| 61 | * Since this depends on DW_AT_comp_dir, older gcc will not | ||
| 62 | * embedded it. In that case, this returns NULL. | ||
| 63 | */ | ||
| 64 | const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
| 65 | { | ||
| 66 | Dwarf_Attribute attr; | ||
| 67 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
| 68 | return NULL; | ||
| 69 | return dwarf_formstring(&attr); | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * cu_find_lineinfo - Get a line number and file name for given address | ||
| 74 | * @cu_die: a CU DIE | ||
| 75 | * @addr: An address | ||
| 76 | * @fname: a pointer which returns the file name string | ||
| 77 | * @lineno: a pointer which returns the line number | ||
| 78 | * | ||
| 79 | * Find a line number and file name for @addr in @cu_die. | ||
| 80 | */ | ||
| 81 | int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, | ||
| 82 | const char **fname, int *lineno) | ||
| 83 | { | ||
| 84 | Dwarf_Line *line; | ||
| 85 | Dwarf_Addr laddr; | ||
| 86 | |||
| 87 | line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr); | ||
| 88 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | ||
| 89 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | ||
| 90 | *fname = dwarf_linesrc(line, NULL, NULL); | ||
| 91 | if (!*fname) | ||
| 92 | /* line number is useless without filename */ | ||
| 93 | *lineno = 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | return *lineno ?: -ENOENT; | ||
| 97 | } | ||
| 98 | |||
| 99 | /** | ||
| 100 | * die_compare_name - Compare diename and tname | ||
| 101 | * @dw_die: a DIE | ||
| 102 | * @tname: a string of target name | ||
| 103 | * | ||
| 104 | * Compare the name of @dw_die and @tname. Return false if @dw_die has no name. | ||
| 105 | */ | ||
| 106 | bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
| 107 | { | ||
| 108 | const char *name; | ||
| 109 | name = dwarf_diename(dw_die); | ||
| 110 | return name ? (strcmp(tname, name) == 0) : false; | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * die_get_call_lineno - Get callsite line number of inline-function instance | ||
| 115 | * @in_die: a DIE of an inlined function instance | ||
| 116 | * | ||
| 117 | * Get call-site line number of @in_die. This means from where the inline | ||
| 118 | * function is called. | ||
| 119 | */ | ||
| 120 | int die_get_call_lineno(Dwarf_Die *in_die) | ||
| 121 | { | ||
| 122 | Dwarf_Attribute attr; | ||
| 123 | Dwarf_Word ret; | ||
| 124 | |||
| 125 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
| 126 | return -ENOENT; | ||
| 127 | |||
| 128 | dwarf_formudata(&attr, &ret); | ||
| 129 | return (int)ret; | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * die_get_type - Get type DIE | ||
| 134 | * @vr_die: a DIE of a variable | ||
| 135 | * @die_mem: where to store a type DIE | ||
| 136 | * | ||
| 137 | * Get a DIE of the type of given variable (@vr_die), and store | ||
| 138 | * it to die_mem. Return NULL if fails to get a type DIE. | ||
| 139 | */ | ||
| 140 | Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
| 141 | { | ||
| 142 | Dwarf_Attribute attr; | ||
| 143 | |||
| 144 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && | ||
| 145 | dwarf_formref_die(&attr, die_mem)) | ||
| 146 | return die_mem; | ||
| 147 | else | ||
| 148 | return NULL; | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Get a type die, but skip qualifiers */ | ||
| 152 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
| 153 | { | ||
| 154 | int tag; | ||
| 155 | |||
| 156 | do { | ||
| 157 | vr_die = die_get_type(vr_die, die_mem); | ||
| 158 | if (!vr_die) | ||
| 159 | break; | ||
| 160 | tag = dwarf_tag(vr_die); | ||
| 161 | } while (tag == DW_TAG_const_type || | ||
| 162 | tag == DW_TAG_restrict_type || | ||
| 163 | tag == DW_TAG_volatile_type || | ||
| 164 | tag == DW_TAG_shared_type); | ||
| 165 | |||
| 166 | return vr_die; | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * die_get_real_type - Get a type die, but skip qualifiers and typedef | ||
| 171 | * @vr_die: a DIE of a variable | ||
| 172 | * @die_mem: where to store a type DIE | ||
| 173 | * | ||
| 174 | * Get a DIE of the type of given variable (@vr_die), and store | ||
| 175 | * it to die_mem. Return NULL if fails to get a type DIE. | ||
| 176 | * If the type is qualifiers (e.g. const) or typedef, this skips it | ||
| 177 | * and tries to find real type (structure or basic types, e.g. int). | ||
| 178 | */ | ||
| 179 | Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
| 180 | { | ||
| 181 | do { | ||
| 182 | vr_die = __die_get_real_type(vr_die, die_mem); | ||
| 183 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); | ||
| 184 | |||
| 185 | return vr_die; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Get attribute and translate it as a udata */ | ||
| 189 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
| 190 | Dwarf_Word *result) | ||
| 191 | { | ||
| 192 | Dwarf_Attribute attr; | ||
| 193 | |||
| 194 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
| 195 | dwarf_formudata(&attr, result) != 0) | ||
| 196 | return -ENOENT; | ||
| 197 | |||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * die_is_signed_type - Check whether a type DIE is signed or not | ||
| 203 | * @tp_die: a DIE of a type | ||
| 204 | * | ||
| 205 | * Get the encoding of @tp_die and return true if the encoding | ||
| 206 | * is signed. | ||
| 207 | */ | ||
| 208 | bool die_is_signed_type(Dwarf_Die *tp_die) | ||
| 209 | { | ||
| 210 | Dwarf_Word ret; | ||
| 211 | |||
| 212 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) | ||
| 213 | return false; | ||
| 214 | |||
| 215 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
| 216 | ret == DW_ATE_signed_fixed); | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * die_get_data_member_location - Get the data-member offset | ||
| 221 | * @mb_die: a DIE of a member of a data structure | ||
| 222 | * @offs: The offset of the member in the data structure | ||
| 223 | * | ||
| 224 | * Get the offset of @mb_die in the data structure including @mb_die, and | ||
| 225 | * stores result offset to @offs. If any error occurs this returns errno. | ||
| 226 | */ | ||
| 227 | int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | ||
| 228 | { | ||
| 229 | Dwarf_Attribute attr; | ||
| 230 | Dwarf_Op *expr; | ||
| 231 | size_t nexpr; | ||
| 232 | int ret; | ||
| 233 | |||
| 234 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | ||
| 235 | return -ENOENT; | ||
| 236 | |||
| 237 | if (dwarf_formudata(&attr, offs) != 0) { | ||
| 238 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | ||
| 239 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | ||
| 240 | if (ret < 0 || nexpr == 0) | ||
| 241 | return -ENOENT; | ||
| 242 | |||
| 243 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | ||
| 244 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | ||
| 245 | expr[0].atom, nexpr); | ||
| 246 | return -ENOTSUP; | ||
| 247 | } | ||
| 248 | *offs = (Dwarf_Word)expr[0].number; | ||
| 249 | } | ||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | /** | ||
| 254 | * die_find_child - Generic DIE search function in DIE tree | ||
| 255 | * @rt_die: a root DIE | ||
| 256 | * @callback: a callback function | ||
| 257 | * @data: a user data passed to the callback function | ||
| 258 | * @die_mem: a buffer for result DIE | ||
| 259 | * | ||
| 260 | * Trace DIE tree from @rt_die and call @callback for each child DIE. | ||
| 261 | * If @callback returns DIE_FIND_CB_END, this stores the DIE into | ||
| 262 | * @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE, | ||
| 263 | * this continues to trace the tree. Optionally, @callback can return | ||
| 264 | * DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only | ||
| 265 | * the children and trace only the siblings respectively. | ||
| 266 | * Returns NULL if @callback can't find any appropriate DIE. | ||
| 267 | */ | ||
| 268 | Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
| 269 | int (*callback)(Dwarf_Die *, void *), | ||
| 270 | void *data, Dwarf_Die *die_mem) | ||
| 271 | { | ||
| 272 | Dwarf_Die child_die; | ||
| 273 | int ret; | ||
| 274 | |||
| 275 | ret = dwarf_child(rt_die, die_mem); | ||
| 276 | if (ret != 0) | ||
| 277 | return NULL; | ||
| 278 | |||
| 279 | do { | ||
| 280 | ret = callback(die_mem, data); | ||
| 281 | if (ret == DIE_FIND_CB_END) | ||
| 282 | return die_mem; | ||
| 283 | |||
| 284 | if ((ret & DIE_FIND_CB_CHILD) && | ||
| 285 | die_find_child(die_mem, callback, data, &child_die)) { | ||
| 286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
| 287 | return die_mem; | ||
| 288 | } | ||
| 289 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
| 290 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
| 291 | |||
| 292 | return NULL; | ||
| 293 | } | ||
| 294 | |||
| 295 | struct __addr_die_search_param { | ||
| 296 | Dwarf_Addr addr; | ||
| 297 | Dwarf_Die *die_mem; | ||
| 298 | }; | ||
| 299 | |||
| 300 | /* die_find callback for non-inlined function search */ | ||
| 301 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
| 302 | { | ||
| 303 | struct __addr_die_search_param *ad = data; | ||
| 304 | |||
| 305 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
| 306 | dwarf_haspc(fn_die, ad->addr)) { | ||
| 307 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
| 308 | return DWARF_CB_ABORT; | ||
| 309 | } | ||
| 310 | return DWARF_CB_OK; | ||
| 311 | } | ||
| 312 | |||
| 313 | /** | ||
| 314 | * die_find_realfunc - Search a non-inlined function at given address | ||
| 315 | * @cu_die: a CU DIE which including @addr | ||
| 316 | * @addr: target address | ||
| 317 | * @die_mem: a buffer for result DIE | ||
| 318 | * | ||
| 319 | * Search a non-inlined function DIE which includes @addr. Stores the | ||
| 320 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | ||
| 321 | */ | ||
| 322 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
| 323 | Dwarf_Die *die_mem) | ||
| 324 | { | ||
| 325 | struct __addr_die_search_param ad; | ||
| 326 | ad.addr = addr; | ||
| 327 | ad.die_mem = die_mem; | ||
| 328 | /* dwarf_getscopes can't find subprogram. */ | ||
| 329 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
| 330 | return NULL; | ||
| 331 | else | ||
| 332 | return die_mem; | ||
| 333 | } | ||
| 334 | |||
| 335 | /* die_find callback for inline function search */ | ||
| 336 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | ||
| 337 | { | ||
| 338 | Dwarf_Addr *addr = data; | ||
| 339 | |||
| 340 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
| 341 | dwarf_haspc(die_mem, *addr)) | ||
| 342 | return DIE_FIND_CB_END; | ||
| 343 | |||
| 344 | return DIE_FIND_CB_CONTINUE; | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * die_find_inlinefunc - Search an inlined function at given address | ||
| 349 | * @cu_die: a CU DIE which including @addr | ||
| 350 | * @addr: target address | ||
| 351 | * @die_mem: a buffer for result DIE | ||
| 352 | * | ||
| 353 | * Search an inlined function DIE which includes @addr. Stores the | ||
| 354 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | ||
| 355 | * If several inlined functions are expanded recursively, this trace | ||
| 356 | * it and returns deepest one. | ||
| 357 | */ | ||
| 358 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
| 359 | Dwarf_Die *die_mem) | ||
| 360 | { | ||
| 361 | Dwarf_Die tmp_die; | ||
| 362 | |||
| 363 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | ||
| 364 | if (!sp_die) | ||
| 365 | return NULL; | ||
| 366 | |||
| 367 | /* Inlined function could be recursive. Trace it until fail */ | ||
| 368 | while (sp_die) { | ||
| 369 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | ||
| 370 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | ||
| 371 | &tmp_die); | ||
| 372 | } | ||
| 373 | |||
| 374 | return die_mem; | ||
| 375 | } | ||
| 376 | |||
| 377 | /* Line walker internal parameters */ | ||
| 378 | struct __line_walk_param { | ||
| 379 | const char *fname; | ||
| 380 | line_walk_callback_t callback; | ||
| 381 | void *data; | ||
| 382 | int retval; | ||
| 383 | }; | ||
| 384 | |||
| 385 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
| 386 | { | ||
| 387 | struct __line_walk_param *lw = data; | ||
| 388 | Dwarf_Addr addr; | ||
| 389 | int lineno; | ||
| 390 | |||
| 391 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
| 392 | lineno = die_get_call_lineno(in_die); | ||
| 393 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
| 394 | lw->retval = lw->callback(lw->fname, lineno, addr, | ||
| 395 | lw->data); | ||
| 396 | if (lw->retval != 0) | ||
| 397 | return DIE_FIND_CB_END; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | return DIE_FIND_CB_SIBLING; | ||
| 401 | } | ||
| 402 | |||
| 403 | /* Walk on lines of blocks included in given DIE */ | ||
| 404 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
| 405 | line_walk_callback_t callback, void *data) | ||
| 406 | { | ||
| 407 | struct __line_walk_param lw = { | ||
| 408 | .callback = callback, | ||
| 409 | .data = data, | ||
| 410 | .retval = 0, | ||
| 411 | }; | ||
| 412 | Dwarf_Die die_mem; | ||
| 413 | Dwarf_Addr addr; | ||
| 414 | int lineno; | ||
| 415 | |||
| 416 | /* Handle function declaration line */ | ||
| 417 | lw.fname = dwarf_decl_file(sp_die); | ||
| 418 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
| 419 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
| 420 | lw.retval = callback(lw.fname, lineno, addr, data); | ||
| 421 | if (lw.retval != 0) | ||
| 422 | goto done; | ||
| 423 | } | ||
| 424 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
| 425 | done: | ||
| 426 | return lw.retval; | ||
| 427 | } | ||
| 428 | |||
| 429 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
| 430 | { | ||
| 431 | struct __line_walk_param *lw = data; | ||
| 432 | |||
| 433 | lw->retval = __die_walk_funclines(sp_die, lw->callback, lw->data); | ||
| 434 | if (lw->retval != 0) | ||
| 435 | return DWARF_CB_ABORT; | ||
| 436 | |||
| 437 | return DWARF_CB_OK; | ||
| 438 | } | ||
| 439 | |||
| 440 | /** | ||
| 441 | * die_walk_lines - Walk on lines inside given DIE | ||
| 442 | * @rt_die: a root DIE (CU or subprogram) | ||
| 443 | * @callback: callback routine | ||
| 444 | * @data: user data | ||
| 445 | * | ||
| 446 | * Walk on all lines inside given @rt_die and call @callback on each line. | ||
| 447 | * If the @rt_die is a function, walk only on the lines inside the function, | ||
| 448 | * otherwise @rt_die must be a CU DIE. | ||
| 449 | * Note that this walks not only dwarf line list, but also function entries | ||
| 450 | * and inline call-site. | ||
| 451 | */ | ||
| 452 | int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) | ||
| 453 | { | ||
| 454 | Dwarf_Lines *lines; | ||
| 455 | Dwarf_Line *line; | ||
| 456 | Dwarf_Addr addr; | ||
| 457 | const char *fname; | ||
| 458 | int lineno, ret = 0; | ||
| 459 | Dwarf_Die die_mem, *cu_die; | ||
| 460 | size_t nlines, i; | ||
| 461 | |||
| 462 | /* Get the CU die */ | ||
| 463 | if (dwarf_tag(rt_die) == DW_TAG_subprogram) | ||
| 464 | cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); | ||
| 465 | else | ||
| 466 | cu_die = rt_die; | ||
| 467 | if (!cu_die) { | ||
| 468 | pr_debug2("Failed to get CU from subprogram\n"); | ||
| 469 | return -EINVAL; | ||
| 470 | } | ||
| 471 | |||
| 472 | /* Get lines list in the CU */ | ||
| 473 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
| 474 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
| 475 | return -ENOENT; | ||
| 476 | } | ||
| 477 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
| 478 | |||
| 479 | /* Walk on the lines on lines list */ | ||
| 480 | for (i = 0; i < nlines; i++) { | ||
| 481 | line = dwarf_onesrcline(lines, i); | ||
| 482 | if (line == NULL || | ||
| 483 | dwarf_lineno(line, &lineno) != 0 || | ||
| 484 | dwarf_lineaddr(line, &addr) != 0) { | ||
| 485 | pr_debug2("Failed to get line info. " | ||
| 486 | "Possible error in debuginfo.\n"); | ||
| 487 | continue; | ||
| 488 | } | ||
| 489 | /* Filter lines based on address */ | ||
| 490 | if (rt_die != cu_die) | ||
| 491 | /* | ||
| 492 | * Address filtering | ||
| 493 | * The line is included in given function, and | ||
| 494 | * no inline block includes it. | ||
| 495 | */ | ||
| 496 | if (!dwarf_haspc(rt_die, addr) || | ||
| 497 | die_find_inlinefunc(rt_die, addr, &die_mem)) | ||
| 498 | continue; | ||
| 499 | /* Get source line */ | ||
| 500 | fname = dwarf_linesrc(line, NULL, NULL); | ||
| 501 | |||
| 502 | ret = callback(fname, lineno, addr, data); | ||
| 503 | if (ret != 0) | ||
| 504 | return ret; | ||
| 505 | } | ||
| 506 | |||
| 507 | /* | ||
| 508 | * Dwarf lines doesn't include function declarations and inlined | ||
| 509 | * subroutines. We have to check functions list or given function. | ||
| 510 | */ | ||
| 511 | if (rt_die != cu_die) | ||
| 512 | ret = __die_walk_funclines(rt_die, callback, data); | ||
| 513 | else { | ||
| 514 | struct __line_walk_param param = { | ||
| 515 | .callback = callback, | ||
| 516 | .data = data, | ||
| 517 | .retval = 0, | ||
| 518 | }; | ||
| 519 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
| 520 | ret = param.retval; | ||
| 521 | } | ||
| 522 | |||
| 523 | return ret; | ||
| 524 | } | ||
| 525 | |||
| 526 | struct __find_variable_param { | ||
| 527 | const char *name; | ||
| 528 | Dwarf_Addr addr; | ||
| 529 | }; | ||
| 530 | |||
| 531 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | ||
| 532 | { | ||
| 533 | struct __find_variable_param *fvp = data; | ||
| 534 | int tag; | ||
| 535 | |||
| 536 | tag = dwarf_tag(die_mem); | ||
| 537 | if ((tag == DW_TAG_formal_parameter || | ||
| 538 | tag == DW_TAG_variable) && | ||
| 539 | die_compare_name(die_mem, fvp->name)) | ||
| 540 | return DIE_FIND_CB_END; | ||
| 541 | |||
| 542 | if (dwarf_haspc(die_mem, fvp->addr)) | ||
| 543 | return DIE_FIND_CB_CONTINUE; | ||
| 544 | else | ||
| 545 | return DIE_FIND_CB_SIBLING; | ||
| 546 | } | ||
| 547 | |||
| 548 | /** | ||
| 549 | * die_find_variable_at - Find a given name variable at given address | ||
| 550 | * @sp_die: a function DIE | ||
| 551 | * @name: variable name | ||
| 552 | * @addr: address | ||
| 553 | * @die_mem: a buffer for result DIE | ||
| 554 | * | ||
| 555 | * Find a variable DIE called @name at @addr in @sp_die. | ||
| 556 | */ | ||
| 557 | Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
| 558 | Dwarf_Addr addr, Dwarf_Die *die_mem) | ||
| 559 | { | ||
| 560 | struct __find_variable_param fvp = { .name = name, .addr = addr}; | ||
| 561 | |||
| 562 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, | ||
| 563 | die_mem); | ||
| 564 | } | ||
| 565 | |||
| 566 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | ||
| 567 | { | ||
| 568 | const char *name = data; | ||
| 569 | |||
| 570 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | ||
| 571 | die_compare_name(die_mem, name)) | ||
| 572 | return DIE_FIND_CB_END; | ||
| 573 | |||
| 574 | return DIE_FIND_CB_SIBLING; | ||
| 575 | } | ||
| 576 | |||
| 577 | /** | ||
| 578 | * die_find_member - Find a given name member in a data structure | ||
| 579 | * @st_die: a data structure type DIE | ||
| 580 | * @name: member name | ||
| 581 | * @die_mem: a buffer for result DIE | ||
| 582 | * | ||
| 583 | * Find a member DIE called @name in @st_die. | ||
| 584 | */ | ||
| 585 | Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
| 586 | Dwarf_Die *die_mem) | ||
| 587 | { | ||
| 588 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
| 589 | die_mem); | ||
| 590 | } | ||
| 591 | |||
| 592 | /** | ||
| 593 | * die_get_typename - Get the name of given variable DIE | ||
| 594 | * @vr_die: a variable DIE | ||
| 595 | * @buf: a buffer for result type name | ||
| 596 | * @len: a max-length of @buf | ||
| 597 | * | ||
| 598 | * Get the name of @vr_die and stores it to @buf. Return the actual length | ||
| 599 | * of type name if succeeded. Return -E2BIG if @len is not enough long, and | ||
| 600 | * Return -ENOENT if failed to find type name. | ||
| 601 | * Note that the result will stores typedef name if possible, and stores | ||
| 602 | * "*(function_type)" if the type is a function pointer. | ||
| 603 | */ | ||
| 604 | int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | ||
| 605 | { | ||
| 606 | Dwarf_Die type; | ||
| 607 | int tag, ret, ret2; | ||
| 608 | const char *tmp = ""; | ||
| 609 | |||
| 610 | if (__die_get_real_type(vr_die, &type) == NULL) | ||
| 611 | return -ENOENT; | ||
| 612 | |||
| 613 | tag = dwarf_tag(&type); | ||
| 614 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) | ||
| 615 | tmp = "*"; | ||
| 616 | else if (tag == DW_TAG_subroutine_type) { | ||
| 617 | /* Function pointer */ | ||
| 618 | ret = snprintf(buf, len, "(function_type)"); | ||
| 619 | return (ret >= len) ? -E2BIG : ret; | ||
| 620 | } else { | ||
| 621 | if (!dwarf_diename(&type)) | ||
| 622 | return -ENOENT; | ||
| 623 | if (tag == DW_TAG_union_type) | ||
| 624 | tmp = "union "; | ||
| 625 | else if (tag == DW_TAG_structure_type) | ||
| 626 | tmp = "struct "; | ||
| 627 | /* Write a base name */ | ||
| 628 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | ||
| 629 | return (ret >= len) ? -E2BIG : ret; | ||
| 630 | } | ||
| 631 | ret = die_get_typename(&type, buf, len); | ||
| 632 | if (ret > 0) { | ||
| 633 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); | ||
| 634 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
| 635 | } | ||
| 636 | return ret; | ||
| 637 | } | ||
| 638 | |||
| 639 | /** | ||
| 640 | * die_get_varname - Get the name and type of given variable DIE | ||
| 641 | * @vr_die: a variable DIE | ||
| 642 | * @buf: a buffer for type and variable name | ||
| 643 | * @len: the max-length of @buf | ||
| 644 | * | ||
| 645 | * Get the name and type of @vr_die and stores it in @buf as "type\tname". | ||
| 646 | */ | ||
| 647 | int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) | ||
| 648 | { | ||
| 649 | int ret, ret2; | ||
| 650 | |||
| 651 | ret = die_get_typename(vr_die, buf, len); | ||
| 652 | if (ret < 0) { | ||
| 653 | pr_debug("Failed to get type, make it unknown.\n"); | ||
| 654 | ret = snprintf(buf, len, "(unknown_type)"); | ||
| 655 | } | ||
| 656 | if (ret > 0) { | ||
| 657 | ret2 = snprintf(buf + ret, len - ret, "\t%s", | ||
| 658 | dwarf_diename(vr_die)); | ||
| 659 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
| 660 | } | ||
| 661 | return ret; | ||
| 662 | } | ||
| 663 | |||
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h new file mode 100644 index 000000000000..bc3b21167e70 --- /dev/null +++ b/tools/perf/util/dwarf-aux.h | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | #ifndef _DWARF_AUX_H | ||
| 2 | #define _DWARF_AUX_H | ||
| 3 | /* | ||
| 4 | * dwarf-aux.h : libdw auxiliary interfaces | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <dwarf.h> | ||
| 23 | #include <elfutils/libdw.h> | ||
| 24 | #include <elfutils/libdwfl.h> | ||
| 25 | #include <elfutils/version.h> | ||
| 26 | |||
| 27 | /* Find the realpath of the target file */ | ||
| 28 | extern const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname); | ||
| 29 | |||
| 30 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | ||
| 31 | extern const char *cu_get_comp_dir(Dwarf_Die *cu_die); | ||
| 32 | |||
| 33 | /* Get a line number and file name for given address */ | ||
| 34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | ||
| 35 | const char **fname, int *lineno); | ||
| 36 | |||
| 37 | /* Compare diename and tname */ | ||
| 38 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); | ||
| 39 | |||
| 40 | /* Get callsite line number of inline-function instance */ | ||
| 41 | extern int die_get_call_lineno(Dwarf_Die *in_die); | ||
| 42 | |||
| 43 | /* Get type die */ | ||
| 44 | extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | ||
| 45 | |||
| 46 | /* Get a type die, but skip qualifiers and typedef */ | ||
| 47 | extern Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | ||
| 48 | |||
| 49 | /* Check whether the DIE is signed or not */ | ||
| 50 | extern bool die_is_signed_type(Dwarf_Die *tp_die); | ||
| 51 | |||
| 52 | /* Get data_member_location offset */ | ||
| 53 | extern int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs); | ||
| 54 | |||
| 55 | /* Return values for die_find_child() callbacks */ | ||
| 56 | enum { | ||
| 57 | DIE_FIND_CB_END = 0, /* End of Search */ | ||
| 58 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
| 59 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
| 60 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
| 61 | }; | ||
| 62 | |||
| 63 | /* Search child DIEs */ | ||
| 64 | extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
| 65 | int (*callback)(Dwarf_Die *, void *), | ||
| 66 | void *data, Dwarf_Die *die_mem); | ||
| 67 | |||
| 68 | /* Search a non-inlined function including given address */ | ||
| 69 | extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
| 70 | Dwarf_Die *die_mem); | ||
| 71 | |||
| 72 | /* Search an inlined function including given address */ | ||
| 73 | extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
| 74 | Dwarf_Die *die_mem); | ||
| 75 | |||
| 76 | /* Walker on lines (Note: line number will not be sorted) */ | ||
| 77 | typedef int (* line_walk_callback_t) (const char *fname, int lineno, | ||
| 78 | Dwarf_Addr addr, void *data); | ||
| 79 | |||
| 80 | /* | ||
| 81 | * Walk on lines inside given DIE. If the DIE is a subprogram, walk only on | ||
| 82 | * the lines inside the subprogram, otherwise the DIE must be a CU DIE. | ||
| 83 | */ | ||
| 84 | extern int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, | ||
| 85 | void *data); | ||
| 86 | |||
| 87 | /* Find a variable called 'name' at given address */ | ||
| 88 | extern Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
| 89 | Dwarf_Addr addr, Dwarf_Die *die_mem); | ||
| 90 | |||
| 91 | /* Find a member called 'name' */ | ||
| 92 | extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
| 93 | Dwarf_Die *die_mem); | ||
| 94 | |||
| 95 | /* Get the name of given variable DIE */ | ||
| 96 | extern int die_get_typename(Dwarf_Die *vr_die, char *buf, int len); | ||
| 97 | |||
| 98 | /* Get the name and type of given variable DIE, stored as "type\tname" */ | ||
| 99 | extern int die_get_varname(Dwarf_Die *vr_die, char *buf, int len); | ||
| 100 | #endif | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0239eb87b232..a03a36b7908a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -377,6 +377,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
| 377 | array++; | 377 | array++; |
| 378 | } | 378 | } |
| 379 | 379 | ||
| 380 | data->addr = 0; | ||
| 380 | if (type & PERF_SAMPLE_ADDR) { | 381 | if (type & PERF_SAMPLE_ADDR) { |
| 381 | data->addr = *array; | 382 | data->addr = *array; |
| 382 | array++; | 383 | array++; |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 7e9366e4490b..e9a31554e265 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -61,6 +61,7 @@ struct perf_evsel { | |||
| 61 | off_t id_offset; | 61 | off_t id_offset; |
| 62 | }; | 62 | }; |
| 63 | struct cgroup_sel *cgrp; | 63 | struct cgroup_sel *cgrp; |
| 64 | bool supported; | ||
| 64 | }; | 65 | }; |
| 65 | 66 | ||
| 66 | struct cpu_map; | 67 | struct cpu_map; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index afb0849fe530..cb2959a3fb43 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -877,9 +877,12 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
| 877 | struct perf_evsel *evsel; | 877 | struct perf_evsel *evsel; |
| 878 | off_t tmp; | 878 | off_t tmp; |
| 879 | 879 | ||
| 880 | if (perf_header__getbuffer64(header, fd, &f_attr, sizeof(f_attr))) | 880 | if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) |
| 881 | goto out_errno; | 881 | goto out_errno; |
| 882 | 882 | ||
| 883 | if (header->needs_swap) | ||
| 884 | perf_event__attr_swap(&f_attr.attr); | ||
| 885 | |||
| 883 | tmp = lseek(fd, 0, SEEK_CUR); | 886 | tmp = lseek(fd, 0, SEEK_CUR); |
| 884 | evsel = perf_evsel__new(&f_attr.attr, i); | 887 | evsel = perf_evsel__new(&f_attr.attr, i); |
| 885 | 888 | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 627a02e03c57..677e1da6bb3e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -14,7 +14,8 @@ enum hist_filter { | |||
| 14 | 14 | ||
| 15 | struct callchain_param callchain_param = { | 15 | struct callchain_param callchain_param = { |
| 16 | .mode = CHAIN_GRAPH_REL, | 16 | .mode = CHAIN_GRAPH_REL, |
| 17 | .min_percent = 0.5 | 17 | .min_percent = 0.5, |
| 18 | .order = ORDER_CALLEE | ||
| 18 | }; | 19 | }; |
| 19 | 20 | ||
| 20 | u16 hists__col_len(struct hists *self, enum hist_column col) | 21 | u16 hists__col_len(struct hists *self, enum hist_column col) |
| @@ -846,6 +847,9 @@ print_entries: | |||
| 846 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
| 847 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| 848 | 849 | ||
| 850 | if (h->filtered) | ||
| 851 | continue; | ||
| 852 | |||
| 849 | if (show_displacement) { | 853 | if (show_displacement) { |
| 850 | if (h->pair != NULL) | 854 | if (h->pair != NULL) |
| 851 | displacement = ((long)h->pair->position - | 855 | displacement = ((long)h->pair->position - |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 41982c373faf..4ea7e19f5251 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -86,22 +86,24 @@ static const char *sw_event_names[PERF_COUNT_SW_MAX] = { | |||
| 86 | 86 | ||
| 87 | #define MAX_ALIASES 8 | 87 | #define MAX_ALIASES 8 |
| 88 | 88 | ||
| 89 | static const char *hw_cache[][MAX_ALIASES] = { | 89 | static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = { |
| 90 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, | 90 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, |
| 91 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, | 91 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, |
| 92 | { "LLC", "L2" }, | 92 | { "LLC", "L2", }, |
| 93 | { "dTLB", "d-tlb", "Data-TLB", }, | 93 | { "dTLB", "d-tlb", "Data-TLB", }, |
| 94 | { "iTLB", "i-tlb", "Instruction-TLB", }, | 94 | { "iTLB", "i-tlb", "Instruction-TLB", }, |
| 95 | { "branch", "branches", "bpu", "btb", "bpc", }, | 95 | { "branch", "branches", "bpu", "btb", "bpc", }, |
| 96 | { "node", }, | ||
| 96 | }; | 97 | }; |
| 97 | 98 | ||
| 98 | static const char *hw_cache_op[][MAX_ALIASES] = { | 99 | static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = { |
| 99 | { "load", "loads", "read", }, | 100 | { "load", "loads", "read", }, |
| 100 | { "store", "stores", "write", }, | 101 | { "store", "stores", "write", }, |
| 101 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, | 102 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, |
| 102 | }; | 103 | }; |
| 103 | 104 | ||
| 104 | static const char *hw_cache_result[][MAX_ALIASES] = { | 105 | static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] |
| 106 | [MAX_ALIASES] = { | ||
| 105 | { "refs", "Reference", "ops", "access", }, | 107 | { "refs", "Reference", "ops", "access", }, |
| 106 | { "misses", "miss", }, | 108 | { "misses", "miss", }, |
| 107 | }; | 109 | }; |
| @@ -124,6 +126,7 @@ static unsigned long hw_cache_stat[C(MAX)] = { | |||
| 124 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | 126 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), |
| 125 | [C(ITLB)] = (CACHE_READ), | 127 | [C(ITLB)] = (CACHE_READ), |
| 126 | [C(BPU)] = (CACHE_READ), | 128 | [C(BPU)] = (CACHE_READ), |
| 129 | [C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
| 127 | }; | 130 | }; |
| 128 | 131 | ||
| 129 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ | 132 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ |
| @@ -393,7 +396,7 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | |||
| 393 | PERF_COUNT_HW_CACHE_OP_MAX); | 396 | PERF_COUNT_HW_CACHE_OP_MAX); |
| 394 | if (cache_op >= 0) { | 397 | if (cache_op >= 0) { |
| 395 | if (!is_cache_op_valid(cache_type, cache_op)) | 398 | if (!is_cache_op_valid(cache_type, cache_op)) |
| 396 | return 0; | 399 | return EVT_FAILED; |
| 397 | continue; | 400 | continue; |
| 398 | } | 401 | } |
| 399 | } | 402 | } |
| @@ -475,7 +478,7 @@ parse_single_tracepoint_event(char *sys_name, | |||
| 475 | /* sys + ':' + event + ':' + flags*/ | 478 | /* sys + ':' + event + ':' + flags*/ |
| 476 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 479 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
| 477 | static enum event_result | 480 | static enum event_result |
| 478 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | 481 | parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name, |
| 479 | const char *evt_exp, char *flags) | 482 | const char *evt_exp, char *flags) |
| 480 | { | 483 | { |
| 481 | char evt_path[MAXPATHLEN]; | 484 | char evt_path[MAXPATHLEN]; |
| @@ -509,7 +512,7 @@ parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | |||
| 509 | if (len < 0) | 512 | if (len < 0) |
| 510 | return EVT_FAILED; | 513 | return EVT_FAILED; |
| 511 | 514 | ||
| 512 | if (parse_events(opt, event_opt, 0)) | 515 | if (parse_events(evlist, event_opt, 0)) |
| 513 | return EVT_FAILED; | 516 | return EVT_FAILED; |
| 514 | } | 517 | } |
| 515 | 518 | ||
| @@ -517,7 +520,7 @@ parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | |||
| 517 | } | 520 | } |
| 518 | 521 | ||
| 519 | static enum event_result | 522 | static enum event_result |
| 520 | parse_tracepoint_event(const struct option *opt, const char **strp, | 523 | parse_tracepoint_event(struct perf_evlist *evlist, const char **strp, |
| 521 | struct perf_event_attr *attr) | 524 | struct perf_event_attr *attr) |
| 522 | { | 525 | { |
| 523 | const char *evt_name; | 526 | const char *evt_name; |
| @@ -557,8 +560,8 @@ parse_tracepoint_event(const struct option *opt, const char **strp, | |||
| 557 | return EVT_FAILED; | 560 | return EVT_FAILED; |
| 558 | if (strpbrk(evt_name, "*?")) { | 561 | if (strpbrk(evt_name, "*?")) { |
| 559 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | 562 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
| 560 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, | 563 | return parse_multiple_tracepoint_event(evlist, sys_name, |
| 561 | flags); | 564 | evt_name, flags); |
| 562 | } else { | 565 | } else { |
| 563 | return parse_single_tracepoint_event(sys_name, evt_name, | 566 | return parse_single_tracepoint_event(sys_name, evt_name, |
| 564 | evt_length, attr, strp); | 567 | evt_length, attr, strp); |
| @@ -778,12 +781,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
| 778 | * Symbolic names are (almost) exactly matched. | 781 | * Symbolic names are (almost) exactly matched. |
| 779 | */ | 782 | */ |
| 780 | static enum event_result | 783 | static enum event_result |
| 781 | parse_event_symbols(const struct option *opt, const char **str, | 784 | parse_event_symbols(struct perf_evlist *evlist, const char **str, |
| 782 | struct perf_event_attr *attr) | 785 | struct perf_event_attr *attr) |
| 783 | { | 786 | { |
| 784 | enum event_result ret; | 787 | enum event_result ret; |
| 785 | 788 | ||
| 786 | ret = parse_tracepoint_event(opt, str, attr); | 789 | ret = parse_tracepoint_event(evlist, str, attr); |
| 787 | if (ret != EVT_FAILED) | 790 | if (ret != EVT_FAILED) |
| 788 | goto modifier; | 791 | goto modifier; |
| 789 | 792 | ||
| @@ -822,9 +825,8 @@ modifier: | |||
| 822 | return ret; | 825 | return ret; |
| 823 | } | 826 | } |
| 824 | 827 | ||
| 825 | int parse_events(const struct option *opt, const char *str, int unset __used) | 828 | int parse_events(struct perf_evlist *evlist , const char *str, int unset __used) |
| 826 | { | 829 | { |
| 827 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
| 828 | struct perf_event_attr attr; | 830 | struct perf_event_attr attr; |
| 829 | enum event_result ret; | 831 | enum event_result ret; |
| 830 | const char *ostr; | 832 | const char *ostr; |
| @@ -832,7 +834,7 @@ int parse_events(const struct option *opt, const char *str, int unset __used) | |||
| 832 | for (;;) { | 834 | for (;;) { |
| 833 | ostr = str; | 835 | ostr = str; |
| 834 | memset(&attr, 0, sizeof(attr)); | 836 | memset(&attr, 0, sizeof(attr)); |
| 835 | ret = parse_event_symbols(opt, &str, &attr); | 837 | ret = parse_event_symbols(evlist, &str, &attr); |
| 836 | if (ret == EVT_FAILED) | 838 | if (ret == EVT_FAILED) |
| 837 | return -1; | 839 | return -1; |
| 838 | 840 | ||
| @@ -863,6 +865,13 @@ int parse_events(const struct option *opt, const char *str, int unset __used) | |||
| 863 | return 0; | 865 | return 0; |
| 864 | } | 866 | } |
| 865 | 867 | ||
| 868 | int parse_events_option(const struct option *opt, const char *str, | ||
| 869 | int unset __used) | ||
| 870 | { | ||
| 871 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
| 872 | return parse_events(evlist, str, unset); | ||
| 873 | } | ||
| 874 | |||
| 866 | int parse_filter(const struct option *opt, const char *str, | 875 | int parse_filter(const struct option *opt, const char *str, |
| 867 | int unset __used) | 876 | int unset __used) |
| 868 | { | 877 | { |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 746d3fcbfc2a..2f8e375e038d 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | struct list_head; | 9 | struct list_head; |
| 10 | struct perf_evsel; | 10 | struct perf_evsel; |
| 11 | struct perf_evlist; | ||
| 11 | 12 | ||
| 12 | struct option; | 13 | struct option; |
| 13 | 14 | ||
| @@ -24,7 +25,10 @@ const char *event_type(int type); | |||
| 24 | const char *event_name(struct perf_evsel *event); | 25 | const char *event_name(struct perf_evsel *event); |
| 25 | extern const char *__event_name(int type, u64 config); | 26 | extern const char *__event_name(int type, u64 config); |
| 26 | 27 | ||
| 27 | extern int parse_events(const struct option *opt, const char *str, int unset); | 28 | extern int parse_events_option(const struct option *opt, const char *str, |
| 29 | int unset); | ||
| 30 | extern int parse_events(struct perf_evlist *evlist, const char *str, | ||
| 31 | int unset); | ||
| 28 | extern int parse_filter(const struct option *opt, const char *str, int unset); | 32 | extern int parse_filter(const struct option *opt, const char *str, int unset); |
| 29 | 33 | ||
| 30 | #define EVENTS_HELP_MAX (128*1024) | 34 | #define EVENTS_HELP_MAX (128*1024) |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f0223166e761..b82d54fa2c56 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -117,6 +117,10 @@ static struct map *kernel_get_module_map(const char *module) | |||
| 117 | struct rb_node *nd; | 117 | struct rb_node *nd; |
| 118 | struct map_groups *grp = &machine.kmaps; | 118 | struct map_groups *grp = &machine.kmaps; |
| 119 | 119 | ||
| 120 | /* A file path -- this is an offline module */ | ||
| 121 | if (module && strchr(module, '/')) | ||
| 122 | return machine__new_module(&machine, 0, module); | ||
| 123 | |||
| 120 | if (!module) | 124 | if (!module) |
| 121 | module = "kernel"; | 125 | module = "kernel"; |
| 122 | 126 | ||
| @@ -170,16 +174,24 @@ const char *kernel_get_module_path(const char *module) | |||
| 170 | } | 174 | } |
| 171 | 175 | ||
| 172 | #ifdef DWARF_SUPPORT | 176 | #ifdef DWARF_SUPPORT |
| 173 | static int open_vmlinux(const char *module) | 177 | /* Open new debuginfo of given module */ |
| 178 | static struct debuginfo *open_debuginfo(const char *module) | ||
| 174 | { | 179 | { |
| 175 | const char *path = kernel_get_module_path(module); | 180 | const char *path; |
| 176 | if (!path) { | 181 | |
| 177 | pr_err("Failed to find path of %s module.\n", | 182 | /* A file path -- this is an offline module */ |
| 178 | module ?: "kernel"); | 183 | if (module && strchr(module, '/')) |
| 179 | return -ENOENT; | 184 | path = module; |
| 185 | else { | ||
| 186 | path = kernel_get_module_path(module); | ||
| 187 | |||
| 188 | if (!path) { | ||
| 189 | pr_err("Failed to find path of %s module.\n", | ||
| 190 | module ?: "kernel"); | ||
| 191 | return NULL; | ||
| 192 | } | ||
| 180 | } | 193 | } |
| 181 | pr_debug("Try to open %s\n", path); | 194 | return debuginfo__new(path); |
| 182 | return open(path, O_RDONLY); | ||
| 183 | } | 195 | } |
| 184 | 196 | ||
| 185 | /* | 197 | /* |
| @@ -193,13 +205,24 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
| 193 | struct map *map; | 205 | struct map *map; |
| 194 | u64 addr; | 206 | u64 addr; |
| 195 | int ret = -ENOENT; | 207 | int ret = -ENOENT; |
| 208 | struct debuginfo *dinfo; | ||
| 196 | 209 | ||
| 197 | sym = __find_kernel_function_by_name(tp->symbol, &map); | 210 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
| 198 | if (sym) { | 211 | if (sym) { |
| 199 | addr = map->unmap_ip(map, sym->start + tp->offset); | 212 | addr = map->unmap_ip(map, sym->start + tp->offset); |
| 200 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, | 213 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, |
| 201 | tp->offset, addr); | 214 | tp->offset, addr); |
| 202 | ret = find_perf_probe_point((unsigned long)addr, pp); | 215 | |
| 216 | dinfo = debuginfo__new_online_kernel(addr); | ||
| 217 | if (dinfo) { | ||
| 218 | ret = debuginfo__find_probe_point(dinfo, | ||
| 219 | (unsigned long)addr, pp); | ||
| 220 | debuginfo__delete(dinfo); | ||
| 221 | } else { | ||
| 222 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", | ||
| 223 | addr); | ||
| 224 | ret = -ENOENT; | ||
| 225 | } | ||
| 203 | } | 226 | } |
| 204 | if (ret <= 0) { | 227 | if (ret <= 0) { |
| 205 | pr_debug("Failed to find corresponding probes from " | 228 | pr_debug("Failed to find corresponding probes from " |
| @@ -214,30 +237,70 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
| 214 | return 0; | 237 | return 0; |
| 215 | } | 238 | } |
| 216 | 239 | ||
| 240 | static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, | ||
| 241 | int ntevs, const char *module) | ||
| 242 | { | ||
| 243 | int i, ret = 0; | ||
| 244 | char *tmp; | ||
| 245 | |||
| 246 | if (!module) | ||
| 247 | return 0; | ||
| 248 | |||
| 249 | tmp = strrchr(module, '/'); | ||
| 250 | if (tmp) { | ||
| 251 | /* This is a module path -- get the module name */ | ||
| 252 | module = strdup(tmp + 1); | ||
| 253 | if (!module) | ||
| 254 | return -ENOMEM; | ||
| 255 | tmp = strchr(module, '.'); | ||
| 256 | if (tmp) | ||
| 257 | *tmp = '\0'; | ||
| 258 | tmp = (char *)module; /* For free() */ | ||
| 259 | } | ||
| 260 | |||
| 261 | for (i = 0; i < ntevs; i++) { | ||
| 262 | tevs[i].point.module = strdup(module); | ||
| 263 | if (!tevs[i].point.module) { | ||
| 264 | ret = -ENOMEM; | ||
| 265 | break; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | if (tmp) | ||
| 270 | free(tmp); | ||
| 271 | |||
| 272 | return ret; | ||
| 273 | } | ||
| 274 | |||
| 217 | /* Try to find perf_probe_event with debuginfo */ | 275 | /* Try to find perf_probe_event with debuginfo */ |
| 218 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 276 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
| 219 | struct probe_trace_event **tevs, | 277 | struct probe_trace_event **tevs, |
| 220 | int max_tevs, const char *module) | 278 | int max_tevs, const char *module) |
| 221 | { | 279 | { |
| 222 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 280 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
| 223 | int fd, ntevs; | 281 | struct debuginfo *dinfo = open_debuginfo(module); |
| 282 | int ntevs, ret = 0; | ||
| 224 | 283 | ||
| 225 | fd = open_vmlinux(module); | 284 | if (!dinfo) { |
| 226 | if (fd < 0) { | ||
| 227 | if (need_dwarf) { | 285 | if (need_dwarf) { |
| 228 | pr_warning("Failed to open debuginfo file.\n"); | 286 | pr_warning("Failed to open debuginfo file.\n"); |
| 229 | return fd; | 287 | return -ENOENT; |
| 230 | } | 288 | } |
| 231 | pr_debug("Could not open vmlinux. Try to use symbols.\n"); | 289 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
| 232 | return 0; | 290 | return 0; |
| 233 | } | 291 | } |
| 234 | 292 | ||
| 235 | /* Searching trace events corresponding to probe event */ | 293 | /* Searching trace events corresponding to a probe event */ |
| 236 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); | 294 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); |
| 295 | |||
| 296 | debuginfo__delete(dinfo); | ||
| 237 | 297 | ||
| 238 | if (ntevs > 0) { /* Succeeded to find trace events */ | 298 | if (ntevs > 0) { /* Succeeded to find trace events */ |
| 239 | pr_debug("find %d probe_trace_events.\n", ntevs); | 299 | pr_debug("find %d probe_trace_events.\n", ntevs); |
| 240 | return ntevs; | 300 | if (module) |
| 301 | ret = add_module_to_probe_trace_events(*tevs, ntevs, | ||
| 302 | module); | ||
| 303 | return ret < 0 ? ret : ntevs; | ||
| 241 | } | 304 | } |
| 242 | 305 | ||
| 243 | if (ntevs == 0) { /* No error but failed to find probe point. */ | 306 | if (ntevs == 0) { /* No error but failed to find probe point. */ |
| @@ -371,8 +434,9 @@ int show_line_range(struct line_range *lr, const char *module) | |||
| 371 | { | 434 | { |
| 372 | int l = 1; | 435 | int l = 1; |
| 373 | struct line_node *ln; | 436 | struct line_node *ln; |
| 437 | struct debuginfo *dinfo; | ||
| 374 | FILE *fp; | 438 | FILE *fp; |
| 375 | int fd, ret; | 439 | int ret; |
| 376 | char *tmp; | 440 | char *tmp; |
| 377 | 441 | ||
| 378 | /* Search a line range */ | 442 | /* Search a line range */ |
| @@ -380,13 +444,14 @@ int show_line_range(struct line_range *lr, const char *module) | |||
| 380 | if (ret < 0) | 444 | if (ret < 0) |
| 381 | return ret; | 445 | return ret; |
| 382 | 446 | ||
| 383 | fd = open_vmlinux(module); | 447 | dinfo = open_debuginfo(module); |
| 384 | if (fd < 0) { | 448 | if (!dinfo) { |
| 385 | pr_warning("Failed to open debuginfo file.\n"); | 449 | pr_warning("Failed to open debuginfo file.\n"); |
| 386 | return fd; | 450 | return -ENOENT; |
| 387 | } | 451 | } |
| 388 | 452 | ||
| 389 | ret = find_line_range(fd, lr); | 453 | ret = debuginfo__find_line_range(dinfo, lr); |
| 454 | debuginfo__delete(dinfo); | ||
| 390 | if (ret == 0) { | 455 | if (ret == 0) { |
| 391 | pr_warning("Specified source line is not found.\n"); | 456 | pr_warning("Specified source line is not found.\n"); |
| 392 | return -ENOENT; | 457 | return -ENOENT; |
| @@ -448,7 +513,8 @@ end: | |||
| 448 | return ret; | 513 | return ret; |
| 449 | } | 514 | } |
| 450 | 515 | ||
| 451 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | 516 | static int show_available_vars_at(struct debuginfo *dinfo, |
| 517 | struct perf_probe_event *pev, | ||
| 452 | int max_vls, struct strfilter *_filter, | 518 | int max_vls, struct strfilter *_filter, |
| 453 | bool externs) | 519 | bool externs) |
| 454 | { | 520 | { |
| @@ -463,7 +529,8 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
| 463 | return -EINVAL; | 529 | return -EINVAL; |
| 464 | pr_debug("Searching variables at %s\n", buf); | 530 | pr_debug("Searching variables at %s\n", buf); |
| 465 | 531 | ||
| 466 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | 532 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, |
| 533 | max_vls, externs); | ||
| 467 | if (ret <= 0) { | 534 | if (ret <= 0) { |
| 468 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 535 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
| 469 | goto end; | 536 | goto end; |
| @@ -504,24 +571,26 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
| 504 | int max_vls, const char *module, | 571 | int max_vls, const char *module, |
| 505 | struct strfilter *_filter, bool externs) | 572 | struct strfilter *_filter, bool externs) |
| 506 | { | 573 | { |
| 507 | int i, fd, ret = 0; | 574 | int i, ret = 0; |
| 575 | struct debuginfo *dinfo; | ||
| 508 | 576 | ||
| 509 | ret = init_vmlinux(); | 577 | ret = init_vmlinux(); |
| 510 | if (ret < 0) | 578 | if (ret < 0) |
| 511 | return ret; | 579 | return ret; |
| 512 | 580 | ||
| 581 | dinfo = open_debuginfo(module); | ||
| 582 | if (!dinfo) { | ||
| 583 | pr_warning("Failed to open debuginfo file.\n"); | ||
| 584 | return -ENOENT; | ||
| 585 | } | ||
| 586 | |||
| 513 | setup_pager(); | 587 | setup_pager(); |
| 514 | 588 | ||
| 515 | for (i = 0; i < npevs && ret >= 0; i++) { | 589 | for (i = 0; i < npevs && ret >= 0; i++) |
| 516 | fd = open_vmlinux(module); | 590 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, |
| 517 | if (fd < 0) { | ||
| 518 | pr_warning("Failed to open debug information file.\n"); | ||
| 519 | ret = fd; | ||
| 520 | break; | ||
| 521 | } | ||
| 522 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, | ||
| 523 | externs); | 591 | externs); |
| 524 | } | 592 | |
| 593 | debuginfo__delete(dinfo); | ||
| 525 | return ret; | 594 | return ret; |
| 526 | } | 595 | } |
| 527 | 596 | ||
| @@ -990,7 +1059,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | |||
| 990 | 1059 | ||
| 991 | /* Parse probe_events event into struct probe_point */ | 1060 | /* Parse probe_events event into struct probe_point */ |
| 992 | static int parse_probe_trace_command(const char *cmd, | 1061 | static int parse_probe_trace_command(const char *cmd, |
| 993 | struct probe_trace_event *tev) | 1062 | struct probe_trace_event *tev) |
| 994 | { | 1063 | { |
| 995 | struct probe_trace_point *tp = &tev->point; | 1064 | struct probe_trace_point *tp = &tev->point; |
| 996 | char pr; | 1065 | char pr; |
| @@ -1023,8 +1092,14 @@ static int parse_probe_trace_command(const char *cmd, | |||
| 1023 | 1092 | ||
| 1024 | tp->retprobe = (pr == 'r'); | 1093 | tp->retprobe = (pr == 'r'); |
| 1025 | 1094 | ||
| 1026 | /* Scan function name and offset */ | 1095 | /* Scan module name(if there), function name and offset */ |
| 1027 | ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, | 1096 | p = strchr(argv[1], ':'); |
| 1097 | if (p) { | ||
| 1098 | tp->module = strndup(argv[1], p - argv[1]); | ||
| 1099 | p++; | ||
| 1100 | } else | ||
| 1101 | p = argv[1]; | ||
| 1102 | ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol, | ||
| 1028 | &tp->offset); | 1103 | &tp->offset); |
| 1029 | if (ret == 1) | 1104 | if (ret == 1) |
| 1030 | tp->offset = 0; | 1105 | tp->offset = 0; |
| @@ -1269,9 +1344,10 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) | |||
| 1269 | if (buf == NULL) | 1344 | if (buf == NULL) |
| 1270 | return NULL; | 1345 | return NULL; |
| 1271 | 1346 | ||
| 1272 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", | 1347 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", |
| 1273 | tp->retprobe ? 'r' : 'p', | 1348 | tp->retprobe ? 'r' : 'p', |
| 1274 | tev->group, tev->event, | 1349 | tev->group, tev->event, |
| 1350 | tp->module ?: "", tp->module ? ":" : "", | ||
| 1275 | tp->symbol, tp->offset); | 1351 | tp->symbol, tp->offset); |
| 1276 | if (len <= 0) | 1352 | if (len <= 0) |
| 1277 | goto error; | 1353 | goto error; |
| @@ -1378,6 +1454,8 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) | |||
| 1378 | free(tev->group); | 1454 | free(tev->group); |
| 1379 | if (tev->point.symbol) | 1455 | if (tev->point.symbol) |
| 1380 | free(tev->point.symbol); | 1456 | free(tev->point.symbol); |
| 1457 | if (tev->point.module) | ||
| 1458 | free(tev->point.module); | ||
| 1381 | for (i = 0; i < tev->nargs; i++) { | 1459 | for (i = 0; i < tev->nargs; i++) { |
| 1382 | if (tev->args[i].name) | 1460 | if (tev->args[i].name) |
| 1383 | free(tev->args[i].name); | 1461 | free(tev->args[i].name); |
| @@ -1729,7 +1807,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
| 1729 | /* Convert perf_probe_event with debuginfo */ | 1807 | /* Convert perf_probe_event with debuginfo */ |
| 1730 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); | 1808 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); |
| 1731 | if (ret != 0) | 1809 | if (ret != 0) |
| 1732 | return ret; | 1810 | return ret; /* Found in debuginfo or got an error */ |
| 1733 | 1811 | ||
| 1734 | /* Allocate trace event buffer */ | 1812 | /* Allocate trace event buffer */ |
| 1735 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); | 1813 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); |
| @@ -1742,6 +1820,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
| 1742 | ret = -ENOMEM; | 1820 | ret = -ENOMEM; |
| 1743 | goto error; | 1821 | goto error; |
| 1744 | } | 1822 | } |
| 1823 | tev->point.module = strdup(module); | ||
| 1824 | if (tev->point.module == NULL) { | ||
| 1825 | ret = -ENOMEM; | ||
| 1826 | goto error; | ||
| 1827 | } | ||
| 1745 | tev->point.offset = pev->point.offset; | 1828 | tev->point.offset = pev->point.offset; |
| 1746 | tev->point.retprobe = pev->point.retprobe; | 1829 | tev->point.retprobe = pev->point.retprobe; |
| 1747 | tev->nargs = pev->nargs; | 1830 | tev->nargs = pev->nargs; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 3434fc9d79d5..a7dee835f49c 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
| @@ -10,6 +10,7 @@ extern bool probe_event_dry_run; | |||
| 10 | /* kprobe-tracer tracing point */ | 10 | /* kprobe-tracer tracing point */ |
| 11 | struct probe_trace_point { | 11 | struct probe_trace_point { |
| 12 | char *symbol; /* Base symbol */ | 12 | char *symbol; /* Base symbol */ |
| 13 | char *module; /* Module name */ | ||
| 13 | unsigned long offset; /* Offset from symbol */ | 14 | unsigned long offset; /* Offset from symbol */ |
| 14 | bool retprobe; /* Return probe flag */ | 15 | bool retprobe; /* Return probe flag */ |
| 15 | }; | 16 | }; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3b9d0b800d5c..3e44a3e36519 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -43,21 +43,6 @@ | |||
| 43 | /* Kprobe tracer basic type is up to u64 */ | 43 | /* Kprobe tracer basic type is up to u64 */ |
| 44 | #define MAX_BASIC_TYPE_BITS 64 | 44 | #define MAX_BASIC_TYPE_BITS 64 |
| 45 | 45 | ||
| 46 | /* | ||
| 47 | * Compare the tail of two strings. | ||
| 48 | * Return 0 if whole of either string is same as another's tail part. | ||
| 49 | */ | ||
| 50 | static int strtailcmp(const char *s1, const char *s2) | ||
| 51 | { | ||
| 52 | int i1 = strlen(s1); | ||
| 53 | int i2 = strlen(s2); | ||
| 54 | while (--i1 >= 0 && --i2 >= 0) { | ||
| 55 | if (s1[i1] != s2[i2]) | ||
| 56 | return s1[i1] - s2[i2]; | ||
| 57 | } | ||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* Line number list operations */ | 46 | /* Line number list operations */ |
| 62 | 47 | ||
| 63 | /* Add a line to line number list */ | 48 | /* Add a line to line number list */ |
| @@ -131,29 +116,37 @@ static const Dwfl_Callbacks offline_callbacks = { | |||
| 131 | }; | 116 | }; |
| 132 | 117 | ||
| 133 | /* Get a Dwarf from offline image */ | 118 | /* Get a Dwarf from offline image */ |
| 134 | static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) | 119 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, |
| 120 | const char *path) | ||
| 135 | { | 121 | { |
| 136 | Dwfl_Module *mod; | 122 | Dwfl_Module *mod; |
| 137 | Dwarf *dbg = NULL; | 123 | int fd; |
| 138 | 124 | ||
| 139 | if (!dwflp) | 125 | fd = open(path, O_RDONLY); |
| 140 | return NULL; | 126 | if (fd < 0) |
| 127 | return fd; | ||
| 141 | 128 | ||
| 142 | *dwflp = dwfl_begin(&offline_callbacks); | 129 | self->dwfl = dwfl_begin(&offline_callbacks); |
| 143 | if (!*dwflp) | 130 | if (!self->dwfl) |
| 144 | return NULL; | 131 | goto error; |
| 145 | 132 | ||
| 146 | mod = dwfl_report_offline(*dwflp, "", "", fd); | 133 | mod = dwfl_report_offline(self->dwfl, "", "", fd); |
| 147 | if (!mod) | 134 | if (!mod) |
| 148 | goto error; | 135 | goto error; |
| 149 | 136 | ||
| 150 | dbg = dwfl_module_getdwarf(mod, bias); | 137 | self->dbg = dwfl_module_getdwarf(mod, &self->bias); |
| 151 | if (!dbg) { | 138 | if (!self->dbg) |
| 139 | goto error; | ||
| 140 | |||
| 141 | return 0; | ||
| 152 | error: | 142 | error: |
| 153 | dwfl_end(*dwflp); | 143 | if (self->dwfl) |
| 154 | *dwflp = NULL; | 144 | dwfl_end(self->dwfl); |
| 155 | } | 145 | else |
| 156 | return dbg; | 146 | close(fd); |
| 147 | memset(self, 0, sizeof(*self)); | ||
| 148 | |||
| 149 | return -ENOENT; | ||
| 157 | } | 150 | } |
| 158 | 151 | ||
| 159 | #if _ELFUTILS_PREREQ(0, 148) | 152 | #if _ELFUTILS_PREREQ(0, 148) |
| @@ -189,597 +182,81 @@ static const Dwfl_Callbacks kernel_callbacks = { | |||
| 189 | }; | 182 | }; |
| 190 | 183 | ||
| 191 | /* Get a Dwarf from live kernel image */ | 184 | /* Get a Dwarf from live kernel image */ |
| 192 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, | 185 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
| 193 | Dwarf_Addr *bias) | 186 | Dwarf_Addr addr) |
| 194 | { | 187 | { |
| 195 | Dwarf *dbg; | 188 | self->dwfl = dwfl_begin(&kernel_callbacks); |
| 196 | 189 | if (!self->dwfl) | |
| 197 | if (!dwflp) | 190 | return -EINVAL; |
| 198 | return NULL; | ||
| 199 | |||
| 200 | *dwflp = dwfl_begin(&kernel_callbacks); | ||
| 201 | if (!*dwflp) | ||
| 202 | return NULL; | ||
| 203 | 191 | ||
| 204 | /* Load the kernel dwarves: Don't care the result here */ | 192 | /* Load the kernel dwarves: Don't care the result here */ |
| 205 | dwfl_linux_kernel_report_kernel(*dwflp); | 193 | dwfl_linux_kernel_report_kernel(self->dwfl); |
| 206 | dwfl_linux_kernel_report_modules(*dwflp); | 194 | dwfl_linux_kernel_report_modules(self->dwfl); |
| 207 | 195 | ||
| 208 | dbg = dwfl_addrdwarf(*dwflp, addr, bias); | 196 | self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); |
| 209 | /* Here, check whether we could get a real dwarf */ | 197 | /* Here, check whether we could get a real dwarf */ |
| 210 | if (!dbg) { | 198 | if (!self->dbg) { |
| 211 | pr_debug("Failed to find kernel dwarf at %lx\n", | 199 | pr_debug("Failed to find kernel dwarf at %lx\n", |
| 212 | (unsigned long)addr); | 200 | (unsigned long)addr); |
| 213 | dwfl_end(*dwflp); | 201 | dwfl_end(self->dwfl); |
| 214 | *dwflp = NULL; | 202 | memset(self, 0, sizeof(*self)); |
| 203 | return -ENOENT; | ||
| 215 | } | 204 | } |
| 216 | return dbg; | 205 | |
| 206 | return 0; | ||
| 217 | } | 207 | } |
| 218 | #else | 208 | #else |
| 219 | /* With older elfutils, this just support kernel module... */ | 209 | /* With older elfutils, this just support kernel module... */ |
| 220 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp, | 210 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
| 221 | Dwarf_Addr *bias) | 211 | Dwarf_Addr addr __used) |
| 222 | { | 212 | { |
| 223 | int fd; | ||
| 224 | const char *path = kernel_get_module_path("kernel"); | 213 | const char *path = kernel_get_module_path("kernel"); |
| 225 | 214 | ||
| 226 | if (!path) { | 215 | if (!path) { |
| 227 | pr_err("Failed to find vmlinux path\n"); | 216 | pr_err("Failed to find vmlinux path\n"); |
| 228 | return NULL; | 217 | return -ENOENT; |
| 229 | } | 218 | } |
| 230 | 219 | ||
| 231 | pr_debug2("Use file %s for debuginfo\n", path); | 220 | pr_debug2("Use file %s for debuginfo\n", path); |
| 232 | fd = open(path, O_RDONLY); | 221 | return debuginfo__init_offline_dwarf(self, path); |
| 233 | if (fd < 0) | ||
| 234 | return NULL; | ||
| 235 | |||
| 236 | return dwfl_init_offline_dwarf(fd, dwflp, bias); | ||
| 237 | } | 222 | } |
| 238 | #endif | 223 | #endif |
| 239 | 224 | ||
| 240 | /* Dwarf wrappers */ | 225 | struct debuginfo *debuginfo__new(const char *path) |
| 241 | |||
| 242 | /* Find the realpath of the target file. */ | ||
| 243 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
| 244 | { | ||
| 245 | Dwarf_Files *files; | ||
| 246 | size_t nfiles, i; | ||
| 247 | const char *src = NULL; | ||
| 248 | int ret; | ||
| 249 | |||
| 250 | if (!fname) | ||
| 251 | return NULL; | ||
| 252 | |||
| 253 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | ||
| 254 | if (ret != 0) | ||
| 255 | return NULL; | ||
| 256 | |||
| 257 | for (i = 0; i < nfiles; i++) { | ||
| 258 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
| 259 | if (strtailcmp(src, fname) == 0) | ||
| 260 | break; | ||
| 261 | } | ||
| 262 | if (i == nfiles) | ||
| 263 | return NULL; | ||
| 264 | return src; | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | ||
| 268 | static const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
| 269 | { | ||
| 270 | Dwarf_Attribute attr; | ||
| 271 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
| 272 | return NULL; | ||
| 273 | return dwarf_formstring(&attr); | ||
| 274 | } | ||
| 275 | |||
| 276 | /* Get a line number and file name for given address */ | ||
| 277 | static int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | ||
| 278 | const char **fname, int *lineno) | ||
| 279 | { | ||
| 280 | Dwarf_Line *line; | ||
| 281 | Dwarf_Addr laddr; | ||
| 282 | |||
| 283 | line = dwarf_getsrc_die(cudie, (Dwarf_Addr)addr); | ||
| 284 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | ||
| 285 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | ||
| 286 | *fname = dwarf_linesrc(line, NULL, NULL); | ||
| 287 | if (!*fname) | ||
| 288 | /* line number is useless without filename */ | ||
| 289 | *lineno = 0; | ||
| 290 | } | ||
| 291 | |||
| 292 | return *lineno ?: -ENOENT; | ||
| 293 | } | ||
| 294 | |||
| 295 | /* Compare diename and tname */ | ||
| 296 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
| 297 | { | ||
| 298 | const char *name; | ||
| 299 | name = dwarf_diename(dw_die); | ||
| 300 | return name ? (strcmp(tname, name) == 0) : false; | ||
| 301 | } | ||
| 302 | |||
| 303 | /* Get callsite line number of inline-function instance */ | ||
| 304 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
| 305 | { | ||
| 306 | Dwarf_Attribute attr; | ||
| 307 | Dwarf_Word ret; | ||
| 308 | |||
| 309 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
| 310 | return -ENOENT; | ||
| 311 | |||
| 312 | dwarf_formudata(&attr, &ret); | ||
| 313 | return (int)ret; | ||
| 314 | } | ||
| 315 | |||
| 316 | /* Get type die */ | ||
| 317 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
| 318 | { | ||
| 319 | Dwarf_Attribute attr; | ||
| 320 | |||
| 321 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && | ||
| 322 | dwarf_formref_die(&attr, die_mem)) | ||
| 323 | return die_mem; | ||
| 324 | else | ||
| 325 | return NULL; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* Get a type die, but skip qualifiers */ | ||
| 329 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
| 330 | { | ||
| 331 | int tag; | ||
| 332 | |||
| 333 | do { | ||
| 334 | vr_die = die_get_type(vr_die, die_mem); | ||
| 335 | if (!vr_die) | ||
| 336 | break; | ||
| 337 | tag = dwarf_tag(vr_die); | ||
| 338 | } while (tag == DW_TAG_const_type || | ||
| 339 | tag == DW_TAG_restrict_type || | ||
| 340 | tag == DW_TAG_volatile_type || | ||
| 341 | tag == DW_TAG_shared_type); | ||
| 342 | |||
| 343 | return vr_die; | ||
| 344 | } | ||
| 345 | |||
| 346 | /* Get a type die, but skip qualifiers and typedef */ | ||
| 347 | static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
| 348 | { | ||
| 349 | do { | ||
| 350 | vr_die = __die_get_real_type(vr_die, die_mem); | ||
| 351 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); | ||
| 352 | |||
| 353 | return vr_die; | ||
| 354 | } | ||
| 355 | |||
| 356 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
| 357 | Dwarf_Word *result) | ||
| 358 | { | ||
| 359 | Dwarf_Attribute attr; | ||
| 360 | |||
| 361 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
| 362 | dwarf_formudata(&attr, result) != 0) | ||
| 363 | return -ENOENT; | ||
| 364 | |||
| 365 | return 0; | ||
| 366 | } | ||
| 367 | |||
| 368 | static bool die_is_signed_type(Dwarf_Die *tp_die) | ||
| 369 | { | ||
| 370 | Dwarf_Word ret; | ||
| 371 | |||
| 372 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) | ||
| 373 | return false; | ||
| 374 | |||
| 375 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
| 376 | ret == DW_ATE_signed_fixed); | ||
| 377 | } | ||
| 378 | |||
| 379 | static int die_get_byte_size(Dwarf_Die *tp_die) | ||
| 380 | { | ||
| 381 | Dwarf_Word ret; | ||
| 382 | |||
| 383 | if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret)) | ||
| 384 | return 0; | ||
| 385 | |||
| 386 | return (int)ret; | ||
| 387 | } | ||
| 388 | |||
| 389 | static int die_get_bit_size(Dwarf_Die *tp_die) | ||
| 390 | { | ||
| 391 | Dwarf_Word ret; | ||
| 392 | |||
| 393 | if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret)) | ||
| 394 | return 0; | ||
| 395 | |||
| 396 | return (int)ret; | ||
| 397 | } | ||
| 398 | |||
| 399 | static int die_get_bit_offset(Dwarf_Die *tp_die) | ||
| 400 | { | ||
| 401 | Dwarf_Word ret; | ||
| 402 | |||
| 403 | if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret)) | ||
| 404 | return 0; | ||
| 405 | |||
| 406 | return (int)ret; | ||
| 407 | } | ||
| 408 | |||
| 409 | /* Get data_member_location offset */ | ||
| 410 | static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | ||
| 411 | { | ||
| 412 | Dwarf_Attribute attr; | ||
| 413 | Dwarf_Op *expr; | ||
| 414 | size_t nexpr; | ||
| 415 | int ret; | ||
| 416 | |||
| 417 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | ||
| 418 | return -ENOENT; | ||
| 419 | |||
| 420 | if (dwarf_formudata(&attr, offs) != 0) { | ||
| 421 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | ||
| 422 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | ||
| 423 | if (ret < 0 || nexpr == 0) | ||
| 424 | return -ENOENT; | ||
| 425 | |||
| 426 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | ||
| 427 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | ||
| 428 | expr[0].atom, nexpr); | ||
| 429 | return -ENOTSUP; | ||
| 430 | } | ||
| 431 | *offs = (Dwarf_Word)expr[0].number; | ||
| 432 | } | ||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 436 | /* Return values for die_find callbacks */ | ||
| 437 | enum { | ||
| 438 | DIE_FIND_CB_FOUND = 0, /* End of Search */ | ||
| 439 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
| 440 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
| 441 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
| 442 | }; | ||
| 443 | |||
| 444 | /* Search a child die */ | ||
| 445 | static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
| 446 | int (*callback)(Dwarf_Die *, void *), | ||
| 447 | void *data, Dwarf_Die *die_mem) | ||
| 448 | { | 226 | { |
| 449 | Dwarf_Die child_die; | 227 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); |
| 450 | int ret; | 228 | if (!self) |
| 451 | |||
| 452 | ret = dwarf_child(rt_die, die_mem); | ||
| 453 | if (ret != 0) | ||
| 454 | return NULL; | 229 | return NULL; |
| 455 | 230 | ||
| 456 | do { | 231 | if (debuginfo__init_offline_dwarf(self, path) < 0) { |
| 457 | ret = callback(die_mem, data); | 232 | free(self); |
| 458 | if (ret == DIE_FIND_CB_FOUND) | 233 | self = NULL; |
| 459 | return die_mem; | ||
| 460 | |||
| 461 | if ((ret & DIE_FIND_CB_CHILD) && | ||
| 462 | die_find_child(die_mem, callback, data, &child_die)) { | ||
| 463 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
| 464 | return die_mem; | ||
| 465 | } | ||
| 466 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
| 467 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
| 468 | |||
| 469 | return NULL; | ||
| 470 | } | ||
| 471 | |||
| 472 | struct __addr_die_search_param { | ||
| 473 | Dwarf_Addr addr; | ||
| 474 | Dwarf_Die *die_mem; | ||
| 475 | }; | ||
| 476 | |||
| 477 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
| 478 | { | ||
| 479 | struct __addr_die_search_param *ad = data; | ||
| 480 | |||
| 481 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
| 482 | dwarf_haspc(fn_die, ad->addr)) { | ||
| 483 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
| 484 | return DWARF_CB_ABORT; | ||
| 485 | } | 234 | } |
| 486 | return DWARF_CB_OK; | ||
| 487 | } | ||
| 488 | |||
| 489 | /* Search a real subprogram including this line, */ | ||
| 490 | static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
| 491 | Dwarf_Die *die_mem) | ||
| 492 | { | ||
| 493 | struct __addr_die_search_param ad; | ||
| 494 | ad.addr = addr; | ||
| 495 | ad.die_mem = die_mem; | ||
| 496 | /* dwarf_getscopes can't find subprogram. */ | ||
| 497 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
| 498 | return NULL; | ||
| 499 | else | ||
| 500 | return die_mem; | ||
| 501 | } | ||
| 502 | |||
| 503 | /* die_find callback for inline function search */ | ||
| 504 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | ||
| 505 | { | ||
| 506 | Dwarf_Addr *addr = data; | ||
| 507 | |||
| 508 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
| 509 | dwarf_haspc(die_mem, *addr)) | ||
| 510 | return DIE_FIND_CB_FOUND; | ||
| 511 | 235 | ||
| 512 | return DIE_FIND_CB_CONTINUE; | 236 | return self; |
| 513 | } | 237 | } |
| 514 | 238 | ||
| 515 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | 239 | struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) |
| 516 | static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
| 517 | Dwarf_Die *die_mem) | ||
| 518 | { | 240 | { |
| 519 | Dwarf_Die tmp_die; | 241 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); |
| 520 | 242 | if (!self) | |
| 521 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | ||
| 522 | if (!sp_die) | ||
| 523 | return NULL; | 243 | return NULL; |
| 524 | 244 | ||
| 525 | /* Inlined function could be recursive. Trace it until fail */ | 245 | if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { |
| 526 | while (sp_die) { | 246 | free(self); |
| 527 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | 247 | self = NULL; |
| 528 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | ||
| 529 | &tmp_die); | ||
| 530 | } | ||
| 531 | |||
| 532 | return die_mem; | ||
| 533 | } | ||
| 534 | |||
| 535 | /* Walker on lines (Note: line number will not be sorted) */ | ||
| 536 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
| 537 | Dwarf_Addr addr, void *data); | ||
| 538 | |||
| 539 | struct __line_walk_param { | ||
| 540 | const char *fname; | ||
| 541 | line_walk_handler_t handler; | ||
| 542 | void *data; | ||
| 543 | int retval; | ||
| 544 | }; | ||
| 545 | |||
| 546 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
| 547 | { | ||
| 548 | struct __line_walk_param *lw = data; | ||
| 549 | Dwarf_Addr addr; | ||
| 550 | int lineno; | ||
| 551 | |||
| 552 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
| 553 | lineno = die_get_call_lineno(in_die); | ||
| 554 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
| 555 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
| 556 | lw->data); | ||
| 557 | if (lw->retval != 0) | ||
| 558 | return DIE_FIND_CB_FOUND; | ||
| 559 | } | ||
| 560 | } | ||
| 561 | return DIE_FIND_CB_SIBLING; | ||
| 562 | } | ||
| 563 | |||
| 564 | /* Walk on lines of blocks included in given DIE */ | ||
| 565 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
| 566 | line_walk_handler_t handler, void *data) | ||
| 567 | { | ||
| 568 | struct __line_walk_param lw = { | ||
| 569 | .handler = handler, | ||
| 570 | .data = data, | ||
| 571 | .retval = 0, | ||
| 572 | }; | ||
| 573 | Dwarf_Die die_mem; | ||
| 574 | Dwarf_Addr addr; | ||
| 575 | int lineno; | ||
| 576 | |||
| 577 | /* Handle function declaration line */ | ||
| 578 | lw.fname = dwarf_decl_file(sp_die); | ||
| 579 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
| 580 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
| 581 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
| 582 | if (lw.retval != 0) | ||
| 583 | goto done; | ||
| 584 | } | ||
| 585 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
| 586 | done: | ||
| 587 | return lw.retval; | ||
| 588 | } | ||
| 589 | |||
| 590 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
| 591 | { | ||
| 592 | struct __line_walk_param *lw = data; | ||
| 593 | |||
| 594 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
| 595 | if (lw->retval != 0) | ||
| 596 | return DWARF_CB_ABORT; | ||
| 597 | |||
| 598 | return DWARF_CB_OK; | ||
| 599 | } | ||
| 600 | |||
| 601 | /* | ||
| 602 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
| 603 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
| 604 | */ | ||
| 605 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
| 606 | void *data) | ||
| 607 | { | ||
| 608 | Dwarf_Lines *lines; | ||
| 609 | Dwarf_Line *line; | ||
| 610 | Dwarf_Addr addr; | ||
| 611 | const char *fname; | ||
| 612 | int lineno, ret = 0; | ||
| 613 | Dwarf_Die die_mem, *cu_die; | ||
| 614 | size_t nlines, i; | ||
| 615 | |||
| 616 | /* Get the CU die */ | ||
| 617 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
| 618 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
| 619 | else | ||
| 620 | cu_die = pdie; | ||
| 621 | if (!cu_die) { | ||
| 622 | pr_debug2("Failed to get CU from subprogram\n"); | ||
| 623 | return -EINVAL; | ||
| 624 | } | ||
| 625 | |||
| 626 | /* Get lines list in the CU */ | ||
| 627 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
| 628 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
| 629 | return -ENOENT; | ||
| 630 | } | ||
| 631 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
| 632 | |||
| 633 | /* Walk on the lines on lines list */ | ||
| 634 | for (i = 0; i < nlines; i++) { | ||
| 635 | line = dwarf_onesrcline(lines, i); | ||
| 636 | if (line == NULL || | ||
| 637 | dwarf_lineno(line, &lineno) != 0 || | ||
| 638 | dwarf_lineaddr(line, &addr) != 0) { | ||
| 639 | pr_debug2("Failed to get line info. " | ||
| 640 | "Possible error in debuginfo.\n"); | ||
| 641 | continue; | ||
| 642 | } | ||
| 643 | /* Filter lines based on address */ | ||
| 644 | if (pdie != cu_die) | ||
| 645 | /* | ||
| 646 | * Address filtering | ||
| 647 | * The line is included in given function, and | ||
| 648 | * no inline block includes it. | ||
| 649 | */ | ||
| 650 | if (!dwarf_haspc(pdie, addr) || | ||
| 651 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
| 652 | continue; | ||
| 653 | /* Get source line */ | ||
| 654 | fname = dwarf_linesrc(line, NULL, NULL); | ||
| 655 | |||
| 656 | ret = handler(fname, lineno, addr, data); | ||
| 657 | if (ret != 0) | ||
| 658 | return ret; | ||
| 659 | } | ||
| 660 | |||
| 661 | /* | ||
| 662 | * Dwarf lines doesn't include function declarations and inlined | ||
| 663 | * subroutines. We have to check functions list or given function. | ||
| 664 | */ | ||
| 665 | if (pdie != cu_die) | ||
| 666 | ret = __die_walk_funclines(pdie, handler, data); | ||
| 667 | else { | ||
| 668 | struct __line_walk_param param = { | ||
| 669 | .handler = handler, | ||
| 670 | .data = data, | ||
| 671 | .retval = 0, | ||
| 672 | }; | ||
| 673 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
| 674 | ret = param.retval; | ||
| 675 | } | 248 | } |
| 676 | 249 | ||
| 677 | return ret; | 250 | return self; |
| 678 | } | ||
| 679 | |||
| 680 | struct __find_variable_param { | ||
| 681 | const char *name; | ||
| 682 | Dwarf_Addr addr; | ||
| 683 | }; | ||
| 684 | |||
| 685 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | ||
| 686 | { | ||
| 687 | struct __find_variable_param *fvp = data; | ||
| 688 | int tag; | ||
| 689 | |||
| 690 | tag = dwarf_tag(die_mem); | ||
| 691 | if ((tag == DW_TAG_formal_parameter || | ||
| 692 | tag == DW_TAG_variable) && | ||
| 693 | die_compare_name(die_mem, fvp->name)) | ||
| 694 | return DIE_FIND_CB_FOUND; | ||
| 695 | |||
| 696 | if (dwarf_haspc(die_mem, fvp->addr)) | ||
| 697 | return DIE_FIND_CB_CONTINUE; | ||
| 698 | else | ||
| 699 | return DIE_FIND_CB_SIBLING; | ||
| 700 | } | ||
| 701 | |||
| 702 | /* Find a variable called 'name' at given address */ | ||
| 703 | static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
| 704 | Dwarf_Addr addr, Dwarf_Die *die_mem) | ||
| 705 | { | ||
| 706 | struct __find_variable_param fvp = { .name = name, .addr = addr}; | ||
| 707 | |||
| 708 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, | ||
| 709 | die_mem); | ||
| 710 | } | ||
| 711 | |||
| 712 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | ||
| 713 | { | ||
| 714 | const char *name = data; | ||
| 715 | |||
| 716 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | ||
| 717 | die_compare_name(die_mem, name)) | ||
| 718 | return DIE_FIND_CB_FOUND; | ||
| 719 | |||
| 720 | return DIE_FIND_CB_SIBLING; | ||
| 721 | } | ||
| 722 | |||
| 723 | /* Find a member called 'name' */ | ||
| 724 | static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
| 725 | Dwarf_Die *die_mem) | ||
| 726 | { | ||
| 727 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
| 728 | die_mem); | ||
| 729 | } | ||
| 730 | |||
| 731 | /* Get the name of given variable DIE */ | ||
| 732 | static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | ||
| 733 | { | ||
| 734 | Dwarf_Die type; | ||
| 735 | int tag, ret, ret2; | ||
| 736 | const char *tmp = ""; | ||
| 737 | |||
| 738 | if (__die_get_real_type(vr_die, &type) == NULL) | ||
| 739 | return -ENOENT; | ||
| 740 | |||
| 741 | tag = dwarf_tag(&type); | ||
| 742 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) | ||
| 743 | tmp = "*"; | ||
| 744 | else if (tag == DW_TAG_subroutine_type) { | ||
| 745 | /* Function pointer */ | ||
| 746 | ret = snprintf(buf, len, "(function_type)"); | ||
| 747 | return (ret >= len) ? -E2BIG : ret; | ||
| 748 | } else { | ||
| 749 | if (!dwarf_diename(&type)) | ||
| 750 | return -ENOENT; | ||
| 751 | if (tag == DW_TAG_union_type) | ||
| 752 | tmp = "union "; | ||
| 753 | else if (tag == DW_TAG_structure_type) | ||
| 754 | tmp = "struct "; | ||
| 755 | /* Write a base name */ | ||
| 756 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | ||
| 757 | return (ret >= len) ? -E2BIG : ret; | ||
| 758 | } | ||
| 759 | ret = die_get_typename(&type, buf, len); | ||
| 760 | if (ret > 0) { | ||
| 761 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); | ||
| 762 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
| 763 | } | ||
| 764 | return ret; | ||
| 765 | } | 251 | } |
| 766 | 252 | ||
| 767 | /* Get the name and type of given variable DIE, stored as "type\tname" */ | 253 | void debuginfo__delete(struct debuginfo *self) |
| 768 | static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) | ||
| 769 | { | 254 | { |
| 770 | int ret, ret2; | 255 | if (self) { |
| 771 | 256 | if (self->dwfl) | |
| 772 | ret = die_get_typename(vr_die, buf, len); | 257 | dwfl_end(self->dwfl); |
| 773 | if (ret < 0) { | 258 | free(self); |
| 774 | pr_debug("Failed to get type, make it unknown.\n"); | ||
| 775 | ret = snprintf(buf, len, "(unknown_type)"); | ||
| 776 | } | 259 | } |
| 777 | if (ret > 0) { | ||
| 778 | ret2 = snprintf(buf + ret, len - ret, "\t%s", | ||
| 779 | dwarf_diename(vr_die)); | ||
| 780 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
| 781 | } | ||
| 782 | return ret; | ||
| 783 | } | 260 | } |
| 784 | 261 | ||
| 785 | /* | 262 | /* |
| @@ -897,6 +374,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
| 897 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | 374 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
| 898 | Dwarf_Die type; | 375 | Dwarf_Die type; |
| 899 | char buf[16]; | 376 | char buf[16]; |
| 377 | int bsize, boffs, total; | ||
| 900 | int ret; | 378 | int ret; |
| 901 | 379 | ||
| 902 | /* TODO: check all types */ | 380 | /* TODO: check all types */ |
| @@ -906,11 +384,15 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
| 906 | return (tvar->type == NULL) ? -ENOMEM : 0; | 384 | return (tvar->type == NULL) ? -ENOMEM : 0; |
| 907 | } | 385 | } |
| 908 | 386 | ||
| 909 | if (die_get_bit_size(vr_die) != 0) { | 387 | bsize = dwarf_bitsize(vr_die); |
| 388 | if (bsize > 0) { | ||
| 910 | /* This is a bitfield */ | 389 | /* This is a bitfield */ |
| 911 | ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die), | 390 | boffs = dwarf_bitoffset(vr_die); |
| 912 | die_get_bit_offset(vr_die), | 391 | total = dwarf_bytesize(vr_die); |
| 913 | BYTES_TO_BITS(die_get_byte_size(vr_die))); | 392 | if (boffs < 0 || total < 0) |
| 393 | return -ENOENT; | ||
| 394 | ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs, | ||
| 395 | BYTES_TO_BITS(total)); | ||
| 914 | goto formatted; | 396 | goto formatted; |
| 915 | } | 397 | } |
| 916 | 398 | ||
| @@ -958,10 +440,11 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
| 958 | return (tvar->type == NULL) ? -ENOMEM : 0; | 440 | return (tvar->type == NULL) ? -ENOMEM : 0; |
| 959 | } | 441 | } |
| 960 | 442 | ||
| 961 | ret = BYTES_TO_BITS(die_get_byte_size(&type)); | 443 | ret = dwarf_bytesize(&type); |
| 962 | if (!ret) | 444 | if (ret <= 0) |
| 963 | /* No size ... try to use default type */ | 445 | /* No size ... try to use default type */ |
| 964 | return 0; | 446 | return 0; |
| 447 | ret = BYTES_TO_BITS(ret); | ||
| 965 | 448 | ||
| 966 | /* Check the bitwidth */ | 449 | /* Check the bitwidth */ |
| 967 | if (ret > MAX_BASIC_TYPE_BITS) { | 450 | if (ret > MAX_BASIC_TYPE_BITS) { |
| @@ -1025,7 +508,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
| 1025 | else | 508 | else |
| 1026 | *ref_ptr = ref; | 509 | *ref_ptr = ref; |
| 1027 | } | 510 | } |
| 1028 | ref->offset += die_get_byte_size(&type) * field->index; | 511 | ref->offset += dwarf_bytesize(&type) * field->index; |
| 1029 | if (!field->next) | 512 | if (!field->next) |
| 1030 | /* Save vr_die for converting types */ | 513 | /* Save vr_die for converting types */ |
| 1031 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | 514 | memcpy(die_mem, vr_die, sizeof(*die_mem)); |
| @@ -1245,8 +728,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1245 | 728 | ||
| 1246 | /* If no real subprogram, find a real one */ | 729 | /* If no real subprogram, find a real one */ |
| 1247 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | 730 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { |
| 1248 | sp_die = die_find_real_subprogram(&pf->cu_die, | 731 | sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem); |
| 1249 | pf->addr, &die_mem); | ||
| 1250 | if (!sp_die) { | 732 | if (!sp_die) { |
| 1251 | pr_warning("Failed to find probe point in any " | 733 | pr_warning("Failed to find probe point in any " |
| 1252 | "functions.\n"); | 734 | "functions.\n"); |
| @@ -1504,28 +986,18 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) | |||
| 1504 | } | 986 | } |
| 1505 | 987 | ||
| 1506 | /* Find probe points from debuginfo */ | 988 | /* Find probe points from debuginfo */ |
| 1507 | static int find_probes(int fd, struct probe_finder *pf) | 989 | static int debuginfo__find_probes(struct debuginfo *self, |
| 990 | struct probe_finder *pf) | ||
| 1508 | { | 991 | { |
| 1509 | struct perf_probe_point *pp = &pf->pev->point; | 992 | struct perf_probe_point *pp = &pf->pev->point; |
| 1510 | Dwarf_Off off, noff; | 993 | Dwarf_Off off, noff; |
| 1511 | size_t cuhl; | 994 | size_t cuhl; |
| 1512 | Dwarf_Die *diep; | 995 | Dwarf_Die *diep; |
| 1513 | Dwarf *dbg = NULL; | ||
| 1514 | Dwfl *dwfl; | ||
| 1515 | Dwarf_Addr bias; /* Currently ignored */ | ||
| 1516 | int ret = 0; | 996 | int ret = 0; |
| 1517 | 997 | ||
| 1518 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
| 1519 | if (!dbg) { | ||
| 1520 | pr_warning("No debug information found in the vmlinux - " | ||
| 1521 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
| 1522 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
| 1523 | return -EBADF; | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | #if _ELFUTILS_PREREQ(0, 142) | 998 | #if _ELFUTILS_PREREQ(0, 142) |
| 1527 | /* Get the call frame information from this dwarf */ | 999 | /* Get the call frame information from this dwarf */ |
| 1528 | pf->cfi = dwarf_getcfi(dbg); | 1000 | pf->cfi = dwarf_getcfi(self->dbg); |
| 1529 | #endif | 1001 | #endif |
| 1530 | 1002 | ||
| 1531 | off = 0; | 1003 | off = 0; |
| @@ -1544,7 +1016,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
| 1544 | .data = pf, | 1016 | .data = pf, |
| 1545 | }; | 1017 | }; |
| 1546 | 1018 | ||
| 1547 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1019 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
| 1020 | &pubname_param, 0); | ||
| 1548 | if (pubname_param.found) { | 1021 | if (pubname_param.found) { |
| 1549 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); | 1022 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); |
| 1550 | if (ret) | 1023 | if (ret) |
| @@ -1553,9 +1026,9 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
| 1553 | } | 1026 | } |
| 1554 | 1027 | ||
| 1555 | /* Loop on CUs (Compilation Unit) */ | 1028 | /* Loop on CUs (Compilation Unit) */ |
| 1556 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 1029 | while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
| 1557 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1030 | /* Get the DIE(Debugging Information Entry) of this CU */ |
| 1558 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); | 1031 | diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); |
| 1559 | if (!diep) | 1032 | if (!diep) |
| 1560 | continue; | 1033 | continue; |
| 1561 | 1034 | ||
| @@ -1582,8 +1055,6 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
| 1582 | 1055 | ||
| 1583 | found: | 1056 | found: |
| 1584 | line_list__free(&pf->lcache); | 1057 | line_list__free(&pf->lcache); |
| 1585 | if (dwfl) | ||
| 1586 | dwfl_end(dwfl); | ||
| 1587 | 1058 | ||
| 1588 | return ret; | 1059 | return ret; |
| 1589 | } | 1060 | } |
| @@ -1629,8 +1100,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1629 | } | 1100 | } |
| 1630 | 1101 | ||
| 1631 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 1102 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
| 1632 | int find_probe_trace_events(int fd, struct perf_probe_event *pev, | 1103 | int debuginfo__find_trace_events(struct debuginfo *self, |
| 1633 | struct probe_trace_event **tevs, int max_tevs) | 1104 | struct perf_probe_event *pev, |
| 1105 | struct probe_trace_event **tevs, int max_tevs) | ||
| 1634 | { | 1106 | { |
| 1635 | struct trace_event_finder tf = { | 1107 | struct trace_event_finder tf = { |
| 1636 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1108 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
| @@ -1645,7 +1117,7 @@ int find_probe_trace_events(int fd, struct perf_probe_event *pev, | |||
| 1645 | tf.tevs = *tevs; | 1117 | tf.tevs = *tevs; |
| 1646 | tf.ntevs = 0; | 1118 | tf.ntevs = 0; |
| 1647 | 1119 | ||
| 1648 | ret = find_probes(fd, &tf.pf); | 1120 | ret = debuginfo__find_probes(self, &tf.pf); |
| 1649 | if (ret < 0) { | 1121 | if (ret < 0) { |
| 1650 | free(*tevs); | 1122 | free(*tevs); |
| 1651 | *tevs = NULL; | 1123 | *tevs = NULL; |
| @@ -1739,9 +1211,10 @@ out: | |||
| 1739 | } | 1211 | } |
| 1740 | 1212 | ||
| 1741 | /* Find available variables at given probe point */ | 1213 | /* Find available variables at given probe point */ |
| 1742 | int find_available_vars_at(int fd, struct perf_probe_event *pev, | 1214 | int debuginfo__find_available_vars_at(struct debuginfo *self, |
| 1743 | struct variable_list **vls, int max_vls, | 1215 | struct perf_probe_event *pev, |
| 1744 | bool externs) | 1216 | struct variable_list **vls, |
| 1217 | int max_vls, bool externs) | ||
| 1745 | { | 1218 | { |
| 1746 | struct available_var_finder af = { | 1219 | struct available_var_finder af = { |
| 1747 | .pf = {.pev = pev, .callback = add_available_vars}, | 1220 | .pf = {.pev = pev, .callback = add_available_vars}, |
| @@ -1756,7 +1229,7 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
| 1756 | af.vls = *vls; | 1229 | af.vls = *vls; |
| 1757 | af.nvls = 0; | 1230 | af.nvls = 0; |
| 1758 | 1231 | ||
| 1759 | ret = find_probes(fd, &af.pf); | 1232 | ret = debuginfo__find_probes(self, &af.pf); |
| 1760 | if (ret < 0) { | 1233 | if (ret < 0) { |
| 1761 | /* Free vlist for error */ | 1234 | /* Free vlist for error */ |
| 1762 | while (af.nvls--) { | 1235 | while (af.nvls--) { |
| @@ -1774,28 +1247,19 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
| 1774 | } | 1247 | } |
| 1775 | 1248 | ||
| 1776 | /* Reverse search */ | 1249 | /* Reverse search */ |
| 1777 | int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | 1250 | int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, |
| 1251 | struct perf_probe_point *ppt) | ||
| 1778 | { | 1252 | { |
| 1779 | Dwarf_Die cudie, spdie, indie; | 1253 | Dwarf_Die cudie, spdie, indie; |
| 1780 | Dwarf *dbg = NULL; | 1254 | Dwarf_Addr _addr, baseaddr; |
| 1781 | Dwfl *dwfl = NULL; | ||
| 1782 | Dwarf_Addr _addr, baseaddr, bias = 0; | ||
| 1783 | const char *fname = NULL, *func = NULL, *tmp; | 1255 | const char *fname = NULL, *func = NULL, *tmp; |
| 1784 | int baseline = 0, lineno = 0, ret = 0; | 1256 | int baseline = 0, lineno = 0, ret = 0; |
| 1785 | 1257 | ||
| 1786 | /* Open the live linux kernel */ | ||
| 1787 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); | ||
| 1788 | if (!dbg) { | ||
| 1789 | pr_warning("No debug information found in the vmlinux - " | ||
| 1790 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
| 1791 | ret = -EINVAL; | ||
| 1792 | goto end; | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | /* Adjust address with bias */ | 1258 | /* Adjust address with bias */ |
| 1796 | addr += bias; | 1259 | addr += self->bias; |
| 1260 | |||
| 1797 | /* Find cu die */ | 1261 | /* Find cu die */ |
| 1798 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { | 1262 | if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { |
| 1799 | pr_warning("Failed to find debug information for address %lx\n", | 1263 | pr_warning("Failed to find debug information for address %lx\n", |
| 1800 | addr); | 1264 | addr); |
| 1801 | ret = -EINVAL; | 1265 | ret = -EINVAL; |
| @@ -1807,7 +1271,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |||
| 1807 | /* Don't care whether it failed or not */ | 1271 | /* Don't care whether it failed or not */ |
| 1808 | 1272 | ||
| 1809 | /* Find a corresponding function (name, baseline and baseaddr) */ | 1273 | /* Find a corresponding function (name, baseline and baseaddr) */ |
| 1810 | if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { | 1274 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { |
| 1811 | /* Get function entry information */ | 1275 | /* Get function entry information */ |
| 1812 | tmp = dwarf_diename(&spdie); | 1276 | tmp = dwarf_diename(&spdie); |
| 1813 | if (!tmp || | 1277 | if (!tmp || |
| @@ -1871,8 +1335,6 @@ post: | |||
| 1871 | } | 1335 | } |
| 1872 | } | 1336 | } |
| 1873 | end: | 1337 | end: |
| 1874 | if (dwfl) | ||
| 1875 | dwfl_end(dwfl); | ||
| 1876 | if (ret == 0 && (fname || func)) | 1338 | if (ret == 0 && (fname || func)) |
| 1877 | ret = 1; /* Found a point */ | 1339 | ret = 1; /* Found a point */ |
| 1878 | return ret; | 1340 | return ret; |
| @@ -1982,26 +1444,15 @@ static int find_line_range_by_func(struct line_finder *lf) | |||
| 1982 | return param.retval; | 1444 | return param.retval; |
| 1983 | } | 1445 | } |
| 1984 | 1446 | ||
| 1985 | int find_line_range(int fd, struct line_range *lr) | 1447 | int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) |
| 1986 | { | 1448 | { |
| 1987 | struct line_finder lf = {.lr = lr, .found = 0}; | 1449 | struct line_finder lf = {.lr = lr, .found = 0}; |
| 1988 | int ret = 0; | 1450 | int ret = 0; |
| 1989 | Dwarf_Off off = 0, noff; | 1451 | Dwarf_Off off = 0, noff; |
| 1990 | size_t cuhl; | 1452 | size_t cuhl; |
| 1991 | Dwarf_Die *diep; | 1453 | Dwarf_Die *diep; |
| 1992 | Dwarf *dbg = NULL; | ||
| 1993 | Dwfl *dwfl; | ||
| 1994 | Dwarf_Addr bias; /* Currently ignored */ | ||
| 1995 | const char *comp_dir; | 1454 | const char *comp_dir; |
| 1996 | 1455 | ||
| 1997 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
| 1998 | if (!dbg) { | ||
| 1999 | pr_warning("No debug information found in the vmlinux - " | ||
| 2000 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
| 2001 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
| 2002 | return -EBADF; | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | /* Fastpath: lookup by function name from .debug_pubnames section */ | 1456 | /* Fastpath: lookup by function name from .debug_pubnames section */ |
| 2006 | if (lr->function) { | 1457 | if (lr->function) { |
| 2007 | struct pubname_callback_param pubname_param = { | 1458 | struct pubname_callback_param pubname_param = { |
| @@ -2010,7 +1461,8 @@ int find_line_range(int fd, struct line_range *lr) | |||
| 2010 | struct dwarf_callback_param line_range_param = { | 1461 | struct dwarf_callback_param line_range_param = { |
| 2011 | .data = (void *)&lf, .retval = 0}; | 1462 | .data = (void *)&lf, .retval = 0}; |
| 2012 | 1463 | ||
| 2013 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1464 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
| 1465 | &pubname_param, 0); | ||
| 2014 | if (pubname_param.found) { | 1466 | if (pubname_param.found) { |
| 2015 | line_range_search_cb(&lf.sp_die, &line_range_param); | 1467 | line_range_search_cb(&lf.sp_die, &line_range_param); |
| 2016 | if (lf.found) | 1468 | if (lf.found) |
| @@ -2020,11 +1472,12 @@ int find_line_range(int fd, struct line_range *lr) | |||
| 2020 | 1472 | ||
| 2021 | /* Loop on CUs (Compilation Unit) */ | 1473 | /* Loop on CUs (Compilation Unit) */ |
| 2022 | while (!lf.found && ret >= 0) { | 1474 | while (!lf.found && ret >= 0) { |
| 2023 | if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) | 1475 | if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, |
| 1476 | NULL, NULL, NULL) != 0) | ||
| 2024 | break; | 1477 | break; |
| 2025 | 1478 | ||
| 2026 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1479 | /* Get the DIE(Debugging Information Entry) of this CU */ |
| 2027 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | 1480 | diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); |
| 2028 | if (!diep) | 1481 | if (!diep) |
| 2029 | continue; | 1482 | continue; |
| 2030 | 1483 | ||
| @@ -2058,7 +1511,6 @@ found: | |||
| 2058 | } | 1511 | } |
| 2059 | 1512 | ||
| 2060 | pr_debug("path: %s\n", lr->path); | 1513 | pr_debug("path: %s\n", lr->path); |
| 2061 | dwfl_end(dwfl); | ||
| 2062 | return (ret < 0) ? ret : lf.found; | 1514 | return (ret < 0) ? ret : lf.found; |
| 2063 | } | 1515 | } |
| 2064 | 1516 | ||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 605730a366db..c478b42a2473 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
| @@ -16,27 +16,42 @@ static inline int is_c_varname(const char *name) | |||
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | #ifdef DWARF_SUPPORT | 18 | #ifdef DWARF_SUPPORT |
| 19 | |||
| 20 | #include "dwarf-aux.h" | ||
| 21 | |||
| 22 | /* TODO: export debuginfo data structure even if no dwarf support */ | ||
| 23 | |||
| 24 | /* debug information structure */ | ||
| 25 | struct debuginfo { | ||
| 26 | Dwarf *dbg; | ||
| 27 | Dwfl *dwfl; | ||
| 28 | Dwarf_Addr bias; | ||
| 29 | }; | ||
| 30 | |||
| 31 | extern struct debuginfo *debuginfo__new(const char *path); | ||
| 32 | extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); | ||
| 33 | extern void debuginfo__delete(struct debuginfo *self); | ||
| 34 | |||
| 19 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 35 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
| 20 | extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, | 36 | extern int debuginfo__find_trace_events(struct debuginfo *self, |
| 21 | struct probe_trace_event **tevs, | 37 | struct perf_probe_event *pev, |
| 22 | int max_tevs); | 38 | struct probe_trace_event **tevs, |
| 39 | int max_tevs); | ||
| 23 | 40 | ||
| 24 | /* Find a perf_probe_point from debuginfo */ | 41 | /* Find a perf_probe_point from debuginfo */ |
| 25 | extern int find_perf_probe_point(unsigned long addr, | 42 | extern int debuginfo__find_probe_point(struct debuginfo *self, |
| 26 | struct perf_probe_point *ppt); | 43 | unsigned long addr, |
| 44 | struct perf_probe_point *ppt); | ||
| 27 | 45 | ||
| 28 | /* Find a line range */ | 46 | /* Find a line range */ |
| 29 | extern int find_line_range(int fd, struct line_range *lr); | 47 | extern int debuginfo__find_line_range(struct debuginfo *self, |
| 48 | struct line_range *lr); | ||
| 30 | 49 | ||
| 31 | /* Find available variables */ | 50 | /* Find available variables */ |
| 32 | extern int find_available_vars_at(int fd, struct perf_probe_event *pev, | 51 | extern int debuginfo__find_available_vars_at(struct debuginfo *self, |
| 33 | struct variable_list **vls, int max_points, | 52 | struct perf_probe_event *pev, |
| 34 | bool externs); | 53 | struct variable_list **vls, |
| 35 | 54 | int max_points, bool externs); | |
| 36 | #include <dwarf.h> | ||
| 37 | #include <elfutils/libdw.h> | ||
| 38 | #include <elfutils/libdwfl.h> | ||
| 39 | #include <elfutils/version.h> | ||
| 40 | 55 | ||
| 41 | struct probe_finder { | 56 | struct probe_finder { |
| 42 | struct perf_probe_event *pev; /* Target probe event */ | 57 | struct perf_probe_event *pev; /* Target probe event */ |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a9ac0504aabd..8e0b5a39d8a7 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
| @@ -247,7 +247,7 @@ struct pyrf_cpu_map { | |||
| 247 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, | 247 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, |
| 248 | PyObject *args, PyObject *kwargs) | 248 | PyObject *args, PyObject *kwargs) |
| 249 | { | 249 | { |
| 250 | static char *kwlist[] = { "cpustr", NULL, NULL, }; | 250 | static char *kwlist[] = { "cpustr", NULL }; |
| 251 | char *cpustr = NULL; | 251 | char *cpustr = NULL; |
| 252 | 252 | ||
| 253 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", | 253 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", |
| @@ -316,7 +316,7 @@ struct pyrf_thread_map { | |||
| 316 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | 316 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, |
| 317 | PyObject *args, PyObject *kwargs) | 317 | PyObject *args, PyObject *kwargs) |
| 318 | { | 318 | { |
| 319 | static char *kwlist[] = { "pid", "tid", NULL, NULL, }; | 319 | static char *kwlist[] = { "pid", "tid", NULL }; |
| 320 | int pid = -1, tid = -1; | 320 | int pid = -1, tid = -1; |
| 321 | 321 | ||
| 322 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | 322 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", |
| @@ -418,7 +418,9 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, | |||
| 418 | "wakeup_events", | 418 | "wakeup_events", |
| 419 | "bp_type", | 419 | "bp_type", |
| 420 | "bp_addr", | 420 | "bp_addr", |
| 421 | "bp_len", NULL, NULL, }; | 421 | "bp_len", |
| 422 | NULL | ||
| 423 | }; | ||
| 422 | u64 sample_period = 0; | 424 | u64 sample_period = 0; |
| 423 | u32 disabled = 0, | 425 | u32 disabled = 0, |
| 424 | inherit = 0, | 426 | inherit = 0, |
| @@ -499,7 +501,7 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | |||
| 499 | struct thread_map *threads = NULL; | 501 | struct thread_map *threads = NULL; |
| 500 | PyObject *pcpus = NULL, *pthreads = NULL; | 502 | PyObject *pcpus = NULL, *pthreads = NULL; |
| 501 | int group = 0, inherit = 0; | 503 | int group = 0, inherit = 0; |
| 502 | static char *kwlist[] = {"cpus", "threads", "group", "inherit", NULL, NULL}; | 504 | static char *kwlist[] = { "cpus", "threads", "group", "inherit", NULL }; |
| 503 | 505 | ||
| 504 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, | 506 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, |
| 505 | &pcpus, &pthreads, &group, &inherit)) | 507 | &pcpus, &pthreads, &group, &inherit)) |
| @@ -582,8 +584,7 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, | |||
| 582 | PyObject *args, PyObject *kwargs) | 584 | PyObject *args, PyObject *kwargs) |
| 583 | { | 585 | { |
| 584 | struct perf_evlist *evlist = &pevlist->evlist; | 586 | struct perf_evlist *evlist = &pevlist->evlist; |
| 585 | static char *kwlist[] = {"pages", "overwrite", | 587 | static char *kwlist[] = { "pages", "overwrite", NULL }; |
| 586 | NULL, NULL}; | ||
| 587 | int pages = 128, overwrite = false; | 588 | int pages = 128, overwrite = false; |
| 588 | 589 | ||
| 589 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, | 590 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, |
| @@ -603,7 +604,7 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | |||
| 603 | PyObject *args, PyObject *kwargs) | 604 | PyObject *args, PyObject *kwargs) |
| 604 | { | 605 | { |
| 605 | struct perf_evlist *evlist = &pevlist->evlist; | 606 | struct perf_evlist *evlist = &pevlist->evlist; |
| 606 | static char *kwlist[] = {"timeout", NULL, NULL}; | 607 | static char *kwlist[] = { "timeout", NULL }; |
| 607 | int timeout = -1, n; | 608 | int timeout = -1, n; |
| 608 | 609 | ||
| 609 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | 610 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) |
| @@ -674,7 +675,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
| 674 | struct perf_evlist *evlist = &pevlist->evlist; | 675 | struct perf_evlist *evlist = &pevlist->evlist; |
| 675 | union perf_event *event; | 676 | union perf_event *event; |
| 676 | int sample_id_all = 1, cpu; | 677 | int sample_id_all = 1, cpu; |
| 677 | static char *kwlist[] = {"cpu", "sample_id_all", NULL, NULL}; | 678 | static char *kwlist[] = { "cpu", "sample_id_all", NULL }; |
| 678 | int err; | 679 | int err; |
| 679 | 680 | ||
| 680 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, | 681 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f5a8fbdd3f76..72458d9da5b1 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "session.h" | 12 | #include "session.h" |
| 13 | #include "sort.h" | 13 | #include "sort.h" |
| 14 | #include "util.h" | 14 | #include "util.h" |
| 15 | #include "cpumap.h" | ||
| 15 | 16 | ||
| 16 | static int perf_session__open(struct perf_session *self, bool force) | 17 | static int perf_session__open(struct perf_session *self, bool force) |
| 17 | { | 18 | { |
| @@ -247,9 +248,14 @@ int perf_session__resolve_callchain(struct perf_session *self, | |||
| 247 | callchain_cursor_reset(&self->callchain_cursor); | 248 | callchain_cursor_reset(&self->callchain_cursor); |
| 248 | 249 | ||
| 249 | for (i = 0; i < chain->nr; i++) { | 250 | for (i = 0; i < chain->nr; i++) { |
| 250 | u64 ip = chain->ips[i]; | 251 | u64 ip; |
| 251 | struct addr_location al; | 252 | struct addr_location al; |
| 252 | 253 | ||
| 254 | if (callchain_param.order == ORDER_CALLEE) | ||
| 255 | ip = chain->ips[i]; | ||
| 256 | else | ||
| 257 | ip = chain->ips[chain->nr - i - 1]; | ||
| 258 | |||
| 253 | if (ip >= PERF_CONTEXT_MAX) { | 259 | if (ip >= PERF_CONTEXT_MAX) { |
| 254 | switch (ip) { | 260 | switch (ip) { |
| 255 | case PERF_CONTEXT_HV: | 261 | case PERF_CONTEXT_HV: |
| @@ -407,20 +413,26 @@ static void perf_event__read_swap(union perf_event *event) | |||
| 407 | event->read.id = bswap_64(event->read.id); | 413 | event->read.id = bswap_64(event->read.id); |
| 408 | } | 414 | } |
| 409 | 415 | ||
| 410 | static void perf_event__attr_swap(union perf_event *event) | 416 | /* exported for swapping attributes in file header */ |
| 417 | void perf_event__attr_swap(struct perf_event_attr *attr) | ||
| 418 | { | ||
| 419 | attr->type = bswap_32(attr->type); | ||
| 420 | attr->size = bswap_32(attr->size); | ||
| 421 | attr->config = bswap_64(attr->config); | ||
| 422 | attr->sample_period = bswap_64(attr->sample_period); | ||
| 423 | attr->sample_type = bswap_64(attr->sample_type); | ||
| 424 | attr->read_format = bswap_64(attr->read_format); | ||
| 425 | attr->wakeup_events = bswap_32(attr->wakeup_events); | ||
| 426 | attr->bp_type = bswap_32(attr->bp_type); | ||
| 427 | attr->bp_addr = bswap_64(attr->bp_addr); | ||
| 428 | attr->bp_len = bswap_64(attr->bp_len); | ||
| 429 | } | ||
| 430 | |||
| 431 | static void perf_event__hdr_attr_swap(union perf_event *event) | ||
| 411 | { | 432 | { |
| 412 | size_t size; | 433 | size_t size; |
| 413 | 434 | ||
| 414 | event->attr.attr.type = bswap_32(event->attr.attr.type); | 435 | perf_event__attr_swap(&event->attr.attr); |
| 415 | event->attr.attr.size = bswap_32(event->attr.attr.size); | ||
| 416 | event->attr.attr.config = bswap_64(event->attr.attr.config); | ||
| 417 | event->attr.attr.sample_period = bswap_64(event->attr.attr.sample_period); | ||
| 418 | event->attr.attr.sample_type = bswap_64(event->attr.attr.sample_type); | ||
| 419 | event->attr.attr.read_format = bswap_64(event->attr.attr.read_format); | ||
| 420 | event->attr.attr.wakeup_events = bswap_32(event->attr.attr.wakeup_events); | ||
| 421 | event->attr.attr.bp_type = bswap_32(event->attr.attr.bp_type); | ||
| 422 | event->attr.attr.bp_addr = bswap_64(event->attr.attr.bp_addr); | ||
| 423 | event->attr.attr.bp_len = bswap_64(event->attr.attr.bp_len); | ||
| 424 | 436 | ||
| 425 | size = event->header.size; | 437 | size = event->header.size; |
| 426 | size -= (void *)&event->attr.id - (void *)event; | 438 | size -= (void *)&event->attr.id - (void *)event; |
| @@ -448,7 +460,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
| 448 | [PERF_RECORD_LOST] = perf_event__all64_swap, | 460 | [PERF_RECORD_LOST] = perf_event__all64_swap, |
| 449 | [PERF_RECORD_READ] = perf_event__read_swap, | 461 | [PERF_RECORD_READ] = perf_event__read_swap, |
| 450 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, | 462 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, |
| 451 | [PERF_RECORD_HEADER_ATTR] = perf_event__attr_swap, | 463 | [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, |
| 452 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, | 464 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
| 453 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, | 465 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
| 454 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 466 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
| @@ -708,9 +720,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event, | |||
| 708 | if (!dump_trace) | 720 | if (!dump_trace) |
| 709 | return; | 721 | return; |
| 710 | 722 | ||
| 711 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n", | 723 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n", |
| 712 | event->header.misc, sample->pid, sample->tid, sample->ip, | 724 | event->header.misc, sample->pid, sample->tid, sample->ip, |
| 713 | sample->period); | 725 | sample->period, sample->addr); |
| 714 | 726 | ||
| 715 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) | 727 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) |
| 716 | callchain__printf(sample); | 728 | callchain__printf(sample); |
| @@ -1202,9 +1214,10 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
| 1202 | return NULL; | 1214 | return NULL; |
| 1203 | } | 1215 | } |
| 1204 | 1216 | ||
| 1205 | void perf_session__print_symbols(union perf_event *event, | 1217 | void perf_session__print_ip(union perf_event *event, |
| 1206 | struct perf_sample *sample, | 1218 | struct perf_sample *sample, |
| 1207 | struct perf_session *session) | 1219 | struct perf_session *session, |
| 1220 | int print_sym, int print_dso) | ||
| 1208 | { | 1221 | { |
| 1209 | struct addr_location al; | 1222 | struct addr_location al; |
| 1210 | const char *symname, *dsoname; | 1223 | const char *symname, *dsoname; |
| @@ -1233,32 +1246,83 @@ void perf_session__print_symbols(union perf_event *event, | |||
| 1233 | if (!node) | 1246 | if (!node) |
| 1234 | break; | 1247 | break; |
| 1235 | 1248 | ||
| 1236 | if (node->sym && node->sym->name) | 1249 | printf("\t%16" PRIx64, node->ip); |
| 1237 | symname = node->sym->name; | 1250 | if (print_sym) { |
| 1251 | if (node->sym && node->sym->name) | ||
| 1252 | symname = node->sym->name; | ||
| 1253 | else | ||
| 1254 | symname = ""; | ||
| 1255 | |||
| 1256 | printf(" %s", symname); | ||
| 1257 | } | ||
| 1258 | if (print_dso) { | ||
| 1259 | if (node->map && node->map->dso && node->map->dso->name) | ||
| 1260 | dsoname = node->map->dso->name; | ||
| 1261 | else | ||
| 1262 | dsoname = ""; | ||
| 1263 | |||
| 1264 | printf(" (%s)", dsoname); | ||
| 1265 | } | ||
| 1266 | printf("\n"); | ||
| 1267 | |||
| 1268 | callchain_cursor_advance(cursor); | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | } else { | ||
| 1272 | printf("%16" PRIx64, sample->ip); | ||
| 1273 | if (print_sym) { | ||
| 1274 | if (al.sym && al.sym->name) | ||
| 1275 | symname = al.sym->name; | ||
| 1238 | else | 1276 | else |
| 1239 | symname = ""; | 1277 | symname = ""; |
| 1240 | 1278 | ||
| 1241 | if (node->map && node->map->dso && node->map->dso->name) | 1279 | printf(" %s", symname); |
| 1242 | dsoname = node->map->dso->name; | 1280 | } |
| 1281 | |||
| 1282 | if (print_dso) { | ||
| 1283 | if (al.map && al.map->dso && al.map->dso->name) | ||
| 1284 | dsoname = al.map->dso->name; | ||
| 1243 | else | 1285 | else |
| 1244 | dsoname = ""; | 1286 | dsoname = ""; |
| 1245 | 1287 | ||
| 1246 | printf("\t%16" PRIx64 " %s (%s)\n", node->ip, symname, dsoname); | 1288 | printf(" (%s)", dsoname); |
| 1289 | } | ||
| 1290 | } | ||
| 1291 | } | ||
| 1247 | 1292 | ||
| 1248 | callchain_cursor_advance(cursor); | 1293 | int perf_session__cpu_bitmap(struct perf_session *session, |
| 1294 | const char *cpu_list, unsigned long *cpu_bitmap) | ||
| 1295 | { | ||
| 1296 | int i; | ||
| 1297 | struct cpu_map *map; | ||
| 1298 | |||
| 1299 | for (i = 0; i < PERF_TYPE_MAX; ++i) { | ||
| 1300 | struct perf_evsel *evsel; | ||
| 1301 | |||
| 1302 | evsel = perf_session__find_first_evtype(session, i); | ||
| 1303 | if (!evsel) | ||
| 1304 | continue; | ||
| 1305 | |||
| 1306 | if (!(evsel->attr.sample_type & PERF_SAMPLE_CPU)) { | ||
| 1307 | pr_err("File does not contain CPU events. " | ||
| 1308 | "Remove -c option to proceed.\n"); | ||
| 1309 | return -1; | ||
| 1249 | } | 1310 | } |
| 1311 | } | ||
| 1250 | 1312 | ||
| 1251 | } else { | 1313 | map = cpu_map__new(cpu_list); |
| 1252 | if (al.sym && al.sym->name) | ||
| 1253 | symname = al.sym->name; | ||
| 1254 | else | ||
| 1255 | symname = ""; | ||
| 1256 | 1314 | ||
| 1257 | if (al.map && al.map->dso && al.map->dso->name) | 1315 | for (i = 0; i < map->nr; i++) { |
| 1258 | dsoname = al.map->dso->name; | 1316 | int cpu = map->map[i]; |
| 1259 | else | 1317 | |
| 1260 | dsoname = ""; | 1318 | if (cpu >= MAX_NR_CPUS) { |
| 1319 | pr_err("Requested CPU %d too large. " | ||
| 1320 | "Consider raising MAX_NR_CPUS\n", cpu); | ||
| 1321 | return -1; | ||
| 1322 | } | ||
| 1261 | 1323 | ||
| 1262 | printf("%16" PRIx64 " %s (%s)", al.addr, symname, dsoname); | 1324 | set_bit(cpu, cpu_bitmap); |
| 1263 | } | 1325 | } |
| 1326 | |||
| 1327 | return 0; | ||
| 1264 | } | 1328 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 66d4e1490879..170601e67d6b 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -112,6 +112,7 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, | |||
| 112 | u64 addr); | 112 | u64 addr); |
| 113 | 113 | ||
| 114 | void mem_bswap_64(void *src, int byte_size); | 114 | void mem_bswap_64(void *src, int byte_size); |
| 115 | void perf_event__attr_swap(struct perf_event_attr *attr); | ||
| 115 | 116 | ||
| 116 | int perf_session__create_kernel_maps(struct perf_session *self); | 117 | int perf_session__create_kernel_maps(struct perf_session *self); |
| 117 | 118 | ||
| @@ -167,8 +168,12 @@ static inline int perf_session__parse_sample(struct perf_session *session, | |||
| 167 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 168 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
| 168 | unsigned int type); | 169 | unsigned int type); |
| 169 | 170 | ||
| 170 | void perf_session__print_symbols(union perf_event *event, | 171 | void perf_session__print_ip(union perf_event *event, |
| 171 | struct perf_sample *sample, | 172 | struct perf_sample *sample, |
| 172 | struct perf_session *session); | 173 | struct perf_session *session, |
| 174 | int print_sym, int print_dso); | ||
| 175 | |||
| 176 | int perf_session__cpu_bitmap(struct perf_session *session, | ||
| 177 | const char *cpu_list, unsigned long *cpu_bitmap); | ||
| 173 | 178 | ||
| 174 | #endif /* __PERF_SESSION_H */ | 179 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f44fa541d56e..401e220566fd 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -15,95 +15,6 @@ char * field_sep; | |||
| 15 | 15 | ||
| 16 | LIST_HEAD(hist_entry__sort_list); | 16 | LIST_HEAD(hist_entry__sort_list); |
| 17 | 17 | ||
| 18 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | ||
| 19 | size_t size, unsigned int width); | ||
| 20 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | ||
| 21 | size_t size, unsigned int width); | ||
| 22 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | ||
| 23 | size_t size, unsigned int width); | ||
| 24 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | ||
| 25 | size_t size, unsigned int width); | ||
| 26 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | ||
| 27 | size_t size, unsigned int width); | ||
| 28 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
| 29 | size_t size, unsigned int width); | ||
| 30 | |||
| 31 | struct sort_entry sort_thread = { | ||
| 32 | .se_header = "Command: Pid", | ||
| 33 | .se_cmp = sort__thread_cmp, | ||
| 34 | .se_snprintf = hist_entry__thread_snprintf, | ||
| 35 | .se_width_idx = HISTC_THREAD, | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct sort_entry sort_comm = { | ||
| 39 | .se_header = "Command", | ||
| 40 | .se_cmp = sort__comm_cmp, | ||
| 41 | .se_collapse = sort__comm_collapse, | ||
| 42 | .se_snprintf = hist_entry__comm_snprintf, | ||
| 43 | .se_width_idx = HISTC_COMM, | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct sort_entry sort_dso = { | ||
| 47 | .se_header = "Shared Object", | ||
| 48 | .se_cmp = sort__dso_cmp, | ||
| 49 | .se_snprintf = hist_entry__dso_snprintf, | ||
| 50 | .se_width_idx = HISTC_DSO, | ||
| 51 | }; | ||
| 52 | |||
| 53 | struct sort_entry sort_sym = { | ||
| 54 | .se_header = "Symbol", | ||
| 55 | .se_cmp = sort__sym_cmp, | ||
| 56 | .se_snprintf = hist_entry__sym_snprintf, | ||
| 57 | .se_width_idx = HISTC_SYMBOL, | ||
| 58 | }; | ||
| 59 | |||
| 60 | struct sort_entry sort_parent = { | ||
| 61 | .se_header = "Parent symbol", | ||
| 62 | .se_cmp = sort__parent_cmp, | ||
| 63 | .se_snprintf = hist_entry__parent_snprintf, | ||
| 64 | .se_width_idx = HISTC_PARENT, | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct sort_entry sort_cpu = { | ||
| 68 | .se_header = "CPU", | ||
| 69 | .se_cmp = sort__cpu_cmp, | ||
| 70 | .se_snprintf = hist_entry__cpu_snprintf, | ||
| 71 | .se_width_idx = HISTC_CPU, | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct sort_dimension { | ||
| 75 | const char *name; | ||
| 76 | struct sort_entry *entry; | ||
| 77 | int taken; | ||
| 78 | }; | ||
| 79 | |||
| 80 | static struct sort_dimension sort_dimensions[] = { | ||
| 81 | { .name = "pid", .entry = &sort_thread, }, | ||
| 82 | { .name = "comm", .entry = &sort_comm, }, | ||
| 83 | { .name = "dso", .entry = &sort_dso, }, | ||
| 84 | { .name = "symbol", .entry = &sort_sym, }, | ||
| 85 | { .name = "parent", .entry = &sort_parent, }, | ||
| 86 | { .name = "cpu", .entry = &sort_cpu, }, | ||
| 87 | }; | ||
| 88 | |||
| 89 | int64_t cmp_null(void *l, void *r) | ||
| 90 | { | ||
| 91 | if (!l && !r) | ||
| 92 | return 0; | ||
| 93 | else if (!l) | ||
| 94 | return -1; | ||
| 95 | else | ||
| 96 | return 1; | ||
| 97 | } | ||
| 98 | |||
| 99 | /* --sort pid */ | ||
| 100 | |||
| 101 | int64_t | ||
| 102 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 103 | { | ||
| 104 | return right->thread->pid - left->thread->pid; | ||
| 105 | } | ||
| 106 | |||
| 107 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | 18 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
| 108 | { | 19 | { |
| 109 | int n; | 20 | int n; |
| @@ -125,6 +36,24 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
| 125 | return n; | 36 | return n; |
| 126 | } | 37 | } |
| 127 | 38 | ||
| 39 | static int64_t cmp_null(void *l, void *r) | ||
| 40 | { | ||
| 41 | if (!l && !r) | ||
| 42 | return 0; | ||
| 43 | else if (!l) | ||
| 44 | return -1; | ||
| 45 | else | ||
| 46 | return 1; | ||
| 47 | } | ||
| 48 | |||
| 49 | /* --sort pid */ | ||
| 50 | |||
| 51 | static int64_t | ||
| 52 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 53 | { | ||
| 54 | return right->thread->pid - left->thread->pid; | ||
| 55 | } | ||
| 56 | |||
| 128 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 57 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
| 129 | size_t size, unsigned int width) | 58 | size_t size, unsigned int width) |
| 130 | { | 59 | { |
| @@ -132,15 +61,50 @@ static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | |||
| 132 | self->thread->comm ?: "", self->thread->pid); | 61 | self->thread->comm ?: "", self->thread->pid); |
| 133 | } | 62 | } |
| 134 | 63 | ||
| 64 | struct sort_entry sort_thread = { | ||
| 65 | .se_header = "Command: Pid", | ||
| 66 | .se_cmp = sort__thread_cmp, | ||
| 67 | .se_snprintf = hist_entry__thread_snprintf, | ||
| 68 | .se_width_idx = HISTC_THREAD, | ||
| 69 | }; | ||
| 70 | |||
| 71 | /* --sort comm */ | ||
| 72 | |||
| 73 | static int64_t | ||
| 74 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 75 | { | ||
| 76 | return right->thread->pid - left->thread->pid; | ||
| 77 | } | ||
| 78 | |||
| 79 | static int64_t | ||
| 80 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
| 81 | { | ||
| 82 | char *comm_l = left->thread->comm; | ||
| 83 | char *comm_r = right->thread->comm; | ||
| 84 | |||
| 85 | if (!comm_l || !comm_r) | ||
| 86 | return cmp_null(comm_l, comm_r); | ||
| 87 | |||
| 88 | return strcmp(comm_l, comm_r); | ||
| 89 | } | ||
| 90 | |||
| 135 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | 91 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, |
| 136 | size_t size, unsigned int width) | 92 | size_t size, unsigned int width) |
| 137 | { | 93 | { |
| 138 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 94 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
| 139 | } | 95 | } |
| 140 | 96 | ||
| 97 | struct sort_entry sort_comm = { | ||
| 98 | .se_header = "Command", | ||
| 99 | .se_cmp = sort__comm_cmp, | ||
| 100 | .se_collapse = sort__comm_collapse, | ||
| 101 | .se_snprintf = hist_entry__comm_snprintf, | ||
| 102 | .se_width_idx = HISTC_COMM, | ||
| 103 | }; | ||
| 104 | |||
| 141 | /* --sort dso */ | 105 | /* --sort dso */ |
| 142 | 106 | ||
| 143 | int64_t | 107 | static int64_t |
| 144 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 108 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
| 145 | { | 109 | { |
| 146 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; | 110 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; |
| @@ -173,9 +137,16 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
| 173 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 137 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
| 174 | } | 138 | } |
| 175 | 139 | ||
| 140 | struct sort_entry sort_dso = { | ||
| 141 | .se_header = "Shared Object", | ||
| 142 | .se_cmp = sort__dso_cmp, | ||
| 143 | .se_snprintf = hist_entry__dso_snprintf, | ||
| 144 | .se_width_idx = HISTC_DSO, | ||
| 145 | }; | ||
| 146 | |||
| 176 | /* --sort symbol */ | 147 | /* --sort symbol */ |
| 177 | 148 | ||
| 178 | int64_t | 149 | static int64_t |
| 179 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 150 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
| 180 | { | 151 | { |
| 181 | u64 ip_l, ip_r; | 152 | u64 ip_l, ip_r; |
| @@ -211,29 +182,16 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
| 211 | return ret; | 182 | return ret; |
| 212 | } | 183 | } |
| 213 | 184 | ||
| 214 | /* --sort comm */ | 185 | struct sort_entry sort_sym = { |
| 215 | 186 | .se_header = "Symbol", | |
| 216 | int64_t | 187 | .se_cmp = sort__sym_cmp, |
| 217 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | 188 | .se_snprintf = hist_entry__sym_snprintf, |
| 218 | { | 189 | .se_width_idx = HISTC_SYMBOL, |
| 219 | return right->thread->pid - left->thread->pid; | 190 | }; |
| 220 | } | ||
| 221 | |||
| 222 | int64_t | ||
| 223 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
| 224 | { | ||
| 225 | char *comm_l = left->thread->comm; | ||
| 226 | char *comm_r = right->thread->comm; | ||
| 227 | |||
| 228 | if (!comm_l || !comm_r) | ||
| 229 | return cmp_null(comm_l, comm_r); | ||
| 230 | |||
| 231 | return strcmp(comm_l, comm_r); | ||
| 232 | } | ||
| 233 | 191 | ||
| 234 | /* --sort parent */ | 192 | /* --sort parent */ |
| 235 | 193 | ||
| 236 | int64_t | 194 | static int64_t |
| 237 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | 195 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) |
| 238 | { | 196 | { |
| 239 | struct symbol *sym_l = left->parent; | 197 | struct symbol *sym_l = left->parent; |
| @@ -252,9 +210,16 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | |||
| 252 | self->parent ? self->parent->name : "[other]"); | 210 | self->parent ? self->parent->name : "[other]"); |
| 253 | } | 211 | } |
| 254 | 212 | ||
| 213 | struct sort_entry sort_parent = { | ||
| 214 | .se_header = "Parent symbol", | ||
| 215 | .se_cmp = sort__parent_cmp, | ||
| 216 | .se_snprintf = hist_entry__parent_snprintf, | ||
| 217 | .se_width_idx = HISTC_PARENT, | ||
| 218 | }; | ||
| 219 | |||
| 255 | /* --sort cpu */ | 220 | /* --sort cpu */ |
| 256 | 221 | ||
| 257 | int64_t | 222 | static int64_t |
| 258 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | 223 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) |
| 259 | { | 224 | { |
| 260 | return right->cpu - left->cpu; | 225 | return right->cpu - left->cpu; |
| @@ -266,6 +231,28 @@ static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | |||
| 266 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | 231 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); |
| 267 | } | 232 | } |
| 268 | 233 | ||
| 234 | struct sort_entry sort_cpu = { | ||
| 235 | .se_header = "CPU", | ||
| 236 | .se_cmp = sort__cpu_cmp, | ||
| 237 | .se_snprintf = hist_entry__cpu_snprintf, | ||
| 238 | .se_width_idx = HISTC_CPU, | ||
| 239 | }; | ||
| 240 | |||
| 241 | struct sort_dimension { | ||
| 242 | const char *name; | ||
| 243 | struct sort_entry *entry; | ||
| 244 | int taken; | ||
| 245 | }; | ||
| 246 | |||
| 247 | static struct sort_dimension sort_dimensions[] = { | ||
| 248 | { .name = "pid", .entry = &sort_thread, }, | ||
| 249 | { .name = "comm", .entry = &sort_comm, }, | ||
| 250 | { .name = "dso", .entry = &sort_dso, }, | ||
| 251 | { .name = "symbol", .entry = &sort_sym, }, | ||
| 252 | { .name = "parent", .entry = &sort_parent, }, | ||
| 253 | { .name = "cpu", .entry = &sort_cpu, }, | ||
| 254 | }; | ||
| 255 | |||
| 269 | int sort_dimension__add(const char *tok) | 256 | int sort_dimension__add(const char *tok) |
| 270 | { | 257 | { |
| 271 | unsigned int i; | 258 | unsigned int i; |
| @@ -273,15 +260,9 @@ int sort_dimension__add(const char *tok) | |||
| 273 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 260 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { |
| 274 | struct sort_dimension *sd = &sort_dimensions[i]; | 261 | struct sort_dimension *sd = &sort_dimensions[i]; |
| 275 | 262 | ||
| 276 | if (sd->taken) | ||
| 277 | continue; | ||
| 278 | |||
| 279 | if (strncasecmp(tok, sd->name, strlen(tok))) | 263 | if (strncasecmp(tok, sd->name, strlen(tok))) |
| 280 | continue; | 264 | continue; |
| 281 | 265 | ||
| 282 | if (sd->entry->se_collapse) | ||
| 283 | sort__need_collapse = 1; | ||
| 284 | |||
| 285 | if (sd->entry == &sort_parent) { | 266 | if (sd->entry == &sort_parent) { |
| 286 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 267 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
| 287 | if (ret) { | 268 | if (ret) { |
| @@ -294,6 +275,12 @@ int sort_dimension__add(const char *tok) | |||
| 294 | sort__has_parent = 1; | 275 | sort__has_parent = 1; |
| 295 | } | 276 | } |
| 296 | 277 | ||
| 278 | if (sd->taken) | ||
| 279 | return 0; | ||
| 280 | |||
| 281 | if (sd->entry->se_collapse) | ||
| 282 | sort__need_collapse = 1; | ||
| 283 | |||
| 297 | if (list_empty(&hist_entry__sort_list)) { | 284 | if (list_empty(&hist_entry__sort_list)) { |
| 298 | if (!strcmp(sd->name, "pid")) | 285 | if (!strcmp(sd->name, "pid")) |
| 299 | sort__first_dimension = SORT_PID; | 286 | sort__first_dimension = SORT_PID; |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0b91053a7d11..77d0388ad415 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -103,20 +103,6 @@ extern struct sort_entry sort_thread; | |||
| 103 | extern struct list_head hist_entry__sort_list; | 103 | extern struct list_head hist_entry__sort_list; |
| 104 | 104 | ||
| 105 | void setup_sorting(const char * const usagestr[], const struct option *opts); | 105 | void setup_sorting(const char * const usagestr[], const struct option *opts); |
| 106 | |||
| 107 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | ||
| 108 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | ||
| 109 | extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); | ||
| 110 | extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used); | ||
| 111 | extern int64_t cmp_null(void *, void *); | ||
| 112 | extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *); | ||
| 113 | extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *); | ||
| 114 | extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | ||
| 115 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | ||
| 116 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | ||
| 117 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | ||
| 118 | int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); | ||
| 119 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | ||
| 120 | extern int sort_dimension__add(const char *); | 106 | extern int sort_dimension__add(const char *); |
| 121 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 107 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
| 122 | const char *list_name, FILE *fp); | 108 | const char *list_name, FILE *fp); |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index b9a985dadd08..d5836382ff2c 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -294,3 +294,22 @@ bool strlazymatch(const char *str, const char *pat) | |||
| 294 | { | 294 | { |
| 295 | return __match_glob(str, pat, true); | 295 | return __match_glob(str, pat, true); |
| 296 | } | 296 | } |
| 297 | |||
| 298 | /** | ||
| 299 | * strtailcmp - Compare the tail of two strings | ||
| 300 | * @s1: 1st string to be compared | ||
| 301 | * @s2: 2nd string to be compared | ||
| 302 | * | ||
| 303 | * Return 0 if whole of either string is same as another's tail part. | ||
| 304 | */ | ||
| 305 | int strtailcmp(const char *s1, const char *s2) | ||
| 306 | { | ||
| 307 | int i1 = strlen(s1); | ||
| 308 | int i2 = strlen(s2); | ||
| 309 | while (--i1 >= 0 && --i2 >= 0) { | ||
| 310 | if (s1[i1] != s2[i2]) | ||
| 311 | return s1[i1] - s2[i2]; | ||
| 312 | } | ||
| 313 | return 0; | ||
| 314 | } | ||
| 315 | |||
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 35729f4c40cb..3403f814ad72 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
| @@ -183,106 +183,59 @@ int bigendian(void) | |||
| 183 | return *ptr == 0x01020304; | 183 | return *ptr == 0x01020304; |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | static unsigned long long copy_file_fd(int fd) | 186 | /* unfortunately, you can not stat debugfs or proc files for size */ |
| 187 | static void record_file(const char *file, size_t hdr_sz) | ||
| 187 | { | 188 | { |
| 188 | unsigned long long size = 0; | 189 | unsigned long long size = 0; |
| 189 | char buf[BUFSIZ]; | 190 | char buf[BUFSIZ], *sizep; |
| 190 | int r; | 191 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); |
| 191 | 192 | int r, fd; | |
| 192 | do { | ||
| 193 | r = read(fd, buf, BUFSIZ); | ||
| 194 | if (r > 0) { | ||
| 195 | size += r; | ||
| 196 | write_or_die(buf, r); | ||
| 197 | } | ||
| 198 | } while (r > 0); | ||
| 199 | |||
| 200 | return size; | ||
| 201 | } | ||
| 202 | |||
| 203 | static unsigned long long copy_file(const char *file) | ||
| 204 | { | ||
| 205 | unsigned long long size = 0; | ||
| 206 | int fd; | ||
| 207 | 193 | ||
| 208 | fd = open(file, O_RDONLY); | 194 | fd = open(file, O_RDONLY); |
| 209 | if (fd < 0) | 195 | if (fd < 0) |
| 210 | die("Can't read '%s'", file); | 196 | die("Can't read '%s'", file); |
| 211 | size = copy_file_fd(fd); | ||
| 212 | close(fd); | ||
| 213 | 197 | ||
| 214 | return size; | 198 | /* put in zeros for file size, then fill true size later */ |
| 215 | } | 199 | write_or_die(&size, hdr_sz); |
| 216 | |||
| 217 | static unsigned long get_size_fd(int fd) | ||
| 218 | { | ||
| 219 | unsigned long long size = 0; | ||
| 220 | char buf[BUFSIZ]; | ||
| 221 | int r; | ||
| 222 | 200 | ||
| 223 | do { | 201 | do { |
| 224 | r = read(fd, buf, BUFSIZ); | 202 | r = read(fd, buf, BUFSIZ); |
| 225 | if (r > 0) | 203 | if (r > 0) { |
| 226 | size += r; | 204 | size += r; |
| 205 | write_or_die(buf, r); | ||
| 206 | } | ||
| 227 | } while (r > 0); | 207 | } while (r > 0); |
| 228 | |||
| 229 | lseek(fd, 0, SEEK_SET); | ||
| 230 | |||
| 231 | return size; | ||
| 232 | } | ||
| 233 | |||
| 234 | static unsigned long get_size(const char *file) | ||
| 235 | { | ||
| 236 | unsigned long long size = 0; | ||
| 237 | int fd; | ||
| 238 | |||
| 239 | fd = open(file, O_RDONLY); | ||
| 240 | if (fd < 0) | ||
| 241 | die("Can't read '%s'", file); | ||
| 242 | size = get_size_fd(fd); | ||
| 243 | close(fd); | 208 | close(fd); |
| 244 | 209 | ||
| 245 | return size; | 210 | /* ugh, handle big-endian hdr_size == 4 */ |
| 211 | sizep = (char*)&size; | ||
| 212 | if (bigendian()) | ||
| 213 | sizep += sizeof(u64) - hdr_sz; | ||
| 214 | |||
| 215 | if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) | ||
| 216 | die("writing to %s", output_file); | ||
| 246 | } | 217 | } |
| 247 | 218 | ||
| 248 | static void read_header_files(void) | 219 | static void read_header_files(void) |
| 249 | { | 220 | { |
| 250 | unsigned long long size, check_size; | ||
| 251 | char *path; | 221 | char *path; |
| 252 | int fd; | 222 | struct stat st; |
| 253 | 223 | ||
| 254 | path = get_tracing_file("events/header_page"); | 224 | path = get_tracing_file("events/header_page"); |
| 255 | fd = open(path, O_RDONLY); | 225 | if (stat(path, &st) < 0) |
| 256 | if (fd < 0) | ||
| 257 | die("can't read '%s'", path); | 226 | die("can't read '%s'", path); |
| 258 | 227 | ||
| 259 | /* unfortunately, you can not stat debugfs files for size */ | ||
| 260 | size = get_size_fd(fd); | ||
| 261 | |||
| 262 | write_or_die("header_page", 12); | 228 | write_or_die("header_page", 12); |
| 263 | write_or_die(&size, 8); | 229 | record_file(path, 8); |
| 264 | check_size = copy_file_fd(fd); | ||
| 265 | close(fd); | ||
| 266 | |||
| 267 | if (size != check_size) | ||
| 268 | die("wrong size for '%s' size=%lld read=%lld", | ||
| 269 | path, size, check_size); | ||
| 270 | put_tracing_file(path); | 230 | put_tracing_file(path); |
| 271 | 231 | ||
| 272 | path = get_tracing_file("events/header_event"); | 232 | path = get_tracing_file("events/header_event"); |
| 273 | fd = open(path, O_RDONLY); | 233 | if (stat(path, &st) < 0) |
| 274 | if (fd < 0) | ||
| 275 | die("can't read '%s'", path); | 234 | die("can't read '%s'", path); |
| 276 | 235 | ||
| 277 | size = get_size_fd(fd); | ||
| 278 | |||
| 279 | write_or_die("header_event", 13); | 236 | write_or_die("header_event", 13); |
| 280 | write_or_die(&size, 8); | 237 | record_file(path, 8); |
| 281 | check_size = copy_file_fd(fd); | ||
| 282 | if (size != check_size) | ||
| 283 | die("wrong size for '%s'", path); | ||
| 284 | put_tracing_file(path); | 238 | put_tracing_file(path); |
| 285 | close(fd); | ||
| 286 | } | 239 | } |
| 287 | 240 | ||
| 288 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 241 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
| @@ -298,7 +251,6 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
| 298 | 251 | ||
| 299 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) | 252 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) |
| 300 | { | 253 | { |
| 301 | unsigned long long size, check_size; | ||
| 302 | struct dirent *dent; | 254 | struct dirent *dent; |
| 303 | struct stat st; | 255 | struct stat st; |
| 304 | char *format; | 256 | char *format; |
| @@ -338,14 +290,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
| 338 | sprintf(format, "%s/%s/format", sys, dent->d_name); | 290 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
| 339 | ret = stat(format, &st); | 291 | ret = stat(format, &st); |
| 340 | 292 | ||
| 341 | if (ret >= 0) { | 293 | if (ret >= 0) |
| 342 | /* unfortunately, you can not stat debugfs files for size */ | 294 | record_file(format, 8); |
| 343 | size = get_size(format); | ||
| 344 | write_or_die(&size, 8); | ||
| 345 | check_size = copy_file(format); | ||
| 346 | if (size != check_size) | ||
| 347 | die("error in size of file '%s'", format); | ||
| 348 | } | ||
| 349 | 295 | ||
| 350 | free(format); | 296 | free(format); |
| 351 | } | 297 | } |
| @@ -426,7 +372,7 @@ static void read_event_files(struct tracepoint_path *tps) | |||
| 426 | 372 | ||
| 427 | static void read_proc_kallsyms(void) | 373 | static void read_proc_kallsyms(void) |
| 428 | { | 374 | { |
| 429 | unsigned int size, check_size; | 375 | unsigned int size; |
| 430 | const char *path = "/proc/kallsyms"; | 376 | const char *path = "/proc/kallsyms"; |
| 431 | struct stat st; | 377 | struct stat st; |
| 432 | int ret; | 378 | int ret; |
| @@ -438,17 +384,12 @@ static void read_proc_kallsyms(void) | |||
| 438 | write_or_die(&size, 4); | 384 | write_or_die(&size, 4); |
| 439 | return; | 385 | return; |
| 440 | } | 386 | } |
| 441 | size = get_size(path); | 387 | record_file(path, 4); |
| 442 | write_or_die(&size, 4); | ||
| 443 | check_size = copy_file(path); | ||
| 444 | if (size != check_size) | ||
| 445 | die("error in size of file '%s'", path); | ||
| 446 | |||
| 447 | } | 388 | } |
| 448 | 389 | ||
| 449 | static void read_ftrace_printk(void) | 390 | static void read_ftrace_printk(void) |
| 450 | { | 391 | { |
| 451 | unsigned int size, check_size; | 392 | unsigned int size; |
| 452 | char *path; | 393 | char *path; |
| 453 | struct stat st; | 394 | struct stat st; |
| 454 | int ret; | 395 | int ret; |
| @@ -461,11 +402,8 @@ static void read_ftrace_printk(void) | |||
| 461 | write_or_die(&size, 4); | 402 | write_or_die(&size, 4); |
| 462 | goto out; | 403 | goto out; |
| 463 | } | 404 | } |
| 464 | size = get_size(path); | 405 | record_file(path, 4); |
| 465 | write_or_die(&size, 4); | 406 | |
| 466 | check_size = copy_file(path); | ||
| 467 | if (size != check_size) | ||
| 468 | die("error in size of file '%s'", path); | ||
| 469 | out: | 407 | out: |
| 470 | put_tracing_file(path); | 408 | put_tracing_file(path); |
| 471 | } | 409 | } |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fc784284ac8b..0128906bac88 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -238,6 +238,7 @@ char **argv_split(const char *str, int *argcp); | |||
| 238 | void argv_free(char **argv); | 238 | void argv_free(char **argv); |
| 239 | bool strglobmatch(const char *str, const char *pat); | 239 | bool strglobmatch(const char *str, const char *pat); |
| 240 | bool strlazymatch(const char *str, const char *pat); | 240 | bool strlazymatch(const char *str, const char *pat); |
| 241 | int strtailcmp(const char *s1, const char *s2); | ||
| 241 | unsigned long convert_unit(unsigned long value, char *unit); | 242 | unsigned long convert_unit(unsigned long value, char *unit); |
| 242 | int readn(int fd, void *buf, size_t size); | 243 | int readn(int fd, void *buf, size_t size); |
| 243 | 244 | ||
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index cef28e6632b9..8d02ccb10c59 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl | |||
| @@ -27,7 +27,7 @@ $default{"TEST_TYPE"} = "test"; | |||
| 27 | $default{"BUILD_TYPE"} = "randconfig"; | 27 | $default{"BUILD_TYPE"} = "randconfig"; |
| 28 | $default{"MAKE_CMD"} = "make"; | 28 | $default{"MAKE_CMD"} = "make"; |
| 29 | $default{"TIMEOUT"} = 120; | 29 | $default{"TIMEOUT"} = 120; |
| 30 | $default{"TMP_DIR"} = "/tmp/ktest"; | 30 | $default{"TMP_DIR"} = "/tmp/ktest/\${MACHINE}"; |
| 31 | $default{"SLEEP_TIME"} = 60; # sleep time between tests | 31 | $default{"SLEEP_TIME"} = 60; # sleep time between tests |
| 32 | $default{"BUILD_NOCLEAN"} = 0; | 32 | $default{"BUILD_NOCLEAN"} = 0; |
| 33 | $default{"REBOOT_ON_ERROR"} = 0; | 33 | $default{"REBOOT_ON_ERROR"} = 0; |
| @@ -41,6 +41,7 @@ $default{"CLEAR_LOG"} = 0; | |||
| 41 | $default{"BISECT_MANUAL"} = 0; | 41 | $default{"BISECT_MANUAL"} = 0; |
| 42 | $default{"BISECT_SKIP"} = 1; | 42 | $default{"BISECT_SKIP"} = 1; |
| 43 | $default{"SUCCESS_LINE"} = "login:"; | 43 | $default{"SUCCESS_LINE"} = "login:"; |
| 44 | $default{"DETECT_TRIPLE_FAULT"} = 1; | ||
| 44 | $default{"BOOTED_TIMEOUT"} = 1; | 45 | $default{"BOOTED_TIMEOUT"} = 1; |
| 45 | $default{"DIE_ON_FAILURE"} = 1; | 46 | $default{"DIE_ON_FAILURE"} = 1; |
| 46 | $default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND"; | 47 | $default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND"; |
| @@ -62,6 +63,10 @@ my $output_config; | |||
| 62 | my $test_type; | 63 | my $test_type; |
| 63 | my $build_type; | 64 | my $build_type; |
| 64 | my $build_options; | 65 | my $build_options; |
| 66 | my $pre_build; | ||
| 67 | my $post_build; | ||
| 68 | my $pre_build_die; | ||
| 69 | my $post_build_die; | ||
| 65 | my $reboot_type; | 70 | my $reboot_type; |
| 66 | my $reboot_script; | 71 | my $reboot_script; |
| 67 | my $power_cycle; | 72 | my $power_cycle; |
| @@ -81,12 +86,17 @@ my $make; | |||
| 81 | my $post_install; | 86 | my $post_install; |
| 82 | my $noclean; | 87 | my $noclean; |
| 83 | my $minconfig; | 88 | my $minconfig; |
| 89 | my $start_minconfig; | ||
| 90 | my $start_minconfig_defined; | ||
| 91 | my $output_minconfig; | ||
| 92 | my $ignore_config; | ||
| 84 | my $addconfig; | 93 | my $addconfig; |
| 85 | my $in_bisect = 0; | 94 | my $in_bisect = 0; |
| 86 | my $bisect_bad = ""; | 95 | my $bisect_bad = ""; |
| 87 | my $reverse_bisect; | 96 | my $reverse_bisect; |
| 88 | my $bisect_manual; | 97 | my $bisect_manual; |
| 89 | my $bisect_skip; | 98 | my $bisect_skip; |
| 99 | my $config_bisect_good; | ||
| 90 | my $in_patchcheck = 0; | 100 | my $in_patchcheck = 0; |
| 91 | my $run_test; | 101 | my $run_test; |
| 92 | my $redirect; | 102 | my $redirect; |
| @@ -98,9 +108,12 @@ my $monitor_cnt = 0; | |||
| 98 | my $sleep_time; | 108 | my $sleep_time; |
| 99 | my $bisect_sleep_time; | 109 | my $bisect_sleep_time; |
| 100 | my $patchcheck_sleep_time; | 110 | my $patchcheck_sleep_time; |
| 111 | my $ignore_warnings; | ||
| 101 | my $store_failures; | 112 | my $store_failures; |
| 113 | my $test_name; | ||
| 102 | my $timeout; | 114 | my $timeout; |
| 103 | my $booted_timeout; | 115 | my $booted_timeout; |
| 116 | my $detect_triplefault; | ||
| 104 | my $console; | 117 | my $console; |
| 105 | my $success_line; | 118 | my $success_line; |
| 106 | my $stop_after_success; | 119 | my $stop_after_success; |
| @@ -115,6 +128,7 @@ my $successes = 0; | |||
| 115 | my %entered_configs; | 128 | my %entered_configs; |
| 116 | my %config_help; | 129 | my %config_help; |
| 117 | my %variable; | 130 | my %variable; |
| 131 | my %force_config; | ||
| 118 | 132 | ||
| 119 | $config_help{"MACHINE"} = << "EOF" | 133 | $config_help{"MACHINE"} = << "EOF" |
| 120 | The machine hostname that you will test. | 134 | The machine hostname that you will test. |
| @@ -204,6 +218,26 @@ $config_help{"REBOOT_SCRIPT"} = << "EOF" | |||
| 204 | EOF | 218 | EOF |
| 205 | ; | 219 | ; |
| 206 | 220 | ||
| 221 | sub read_yn { | ||
| 222 | my ($prompt) = @_; | ||
| 223 | |||
| 224 | my $ans; | ||
| 225 | |||
| 226 | for (;;) { | ||
| 227 | print "$prompt [Y/n] "; | ||
| 228 | $ans = <STDIN>; | ||
| 229 | chomp $ans; | ||
| 230 | if ($ans =~ /^\s*$/) { | ||
| 231 | $ans = "y"; | ||
| 232 | } | ||
| 233 | last if ($ans =~ /^y$/i || $ans =~ /^n$/i); | ||
| 234 | print "Please answer either 'y' or 'n'.\n"; | ||
| 235 | } | ||
| 236 | if ($ans !~ /^y$/i) { | ||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | return 1; | ||
| 240 | } | ||
| 207 | 241 | ||
| 208 | sub get_ktest_config { | 242 | sub get_ktest_config { |
| 209 | my ($config) = @_; | 243 | my ($config) = @_; |
| @@ -335,6 +369,7 @@ sub read_config { | |||
| 335 | my $num_tests_set = 0; | 369 | my $num_tests_set = 0; |
| 336 | my $skip = 0; | 370 | my $skip = 0; |
| 337 | my $rest; | 371 | my $rest; |
| 372 | my $test_case = 0; | ||
| 338 | 373 | ||
| 339 | while (<IN>) { | 374 | while (<IN>) { |
| 340 | 375 | ||
| @@ -360,6 +395,7 @@ sub read_config { | |||
| 360 | $rest = $1; | 395 | $rest = $1; |
| 361 | $skip = 1; | 396 | $skip = 1; |
| 362 | } else { | 397 | } else { |
| 398 | $test_case = 1; | ||
| 363 | $skip = 0; | 399 | $skip = 0; |
| 364 | } | 400 | } |
| 365 | 401 | ||
| @@ -464,6 +500,15 @@ sub read_config { | |||
| 464 | # make sure we have all mandatory configs | 500 | # make sure we have all mandatory configs |
| 465 | get_ktest_configs; | 501 | get_ktest_configs; |
| 466 | 502 | ||
| 503 | # was a test specified? | ||
| 504 | if (!$test_case) { | ||
| 505 | print "No test case specified.\n"; | ||
| 506 | print "What test case would you like to run?\n"; | ||
| 507 | my $ans = <STDIN>; | ||
| 508 | chomp $ans; | ||
| 509 | $default{"TEST_TYPE"} = $ans; | ||
| 510 | } | ||
| 511 | |||
| 467 | # set any defaults | 512 | # set any defaults |
| 468 | 513 | ||
| 469 | foreach my $default (keys %default) { | 514 | foreach my $default (keys %default) { |
| @@ -473,6 +518,69 @@ sub read_config { | |||
| 473 | } | 518 | } |
| 474 | } | 519 | } |
| 475 | 520 | ||
| 521 | sub __eval_option { | ||
| 522 | my ($option, $i) = @_; | ||
| 523 | |||
| 524 | # Add space to evaluate the character before $ | ||
| 525 | $option = " $option"; | ||
| 526 | my $retval = ""; | ||
| 527 | |||
| 528 | while ($option =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) { | ||
| 529 | my $start = $1; | ||
| 530 | my $var = $2; | ||
| 531 | my $end = $3; | ||
| 532 | |||
| 533 | # Append beginning of line | ||
| 534 | $retval = "$retval$start"; | ||
| 535 | |||
| 536 | # If the iteration option OPT[$i] exists, then use that. | ||
| 537 | # otherwise see if the default OPT (without [$i]) exists. | ||
| 538 | |||
| 539 | my $o = "$var\[$i\]"; | ||
| 540 | |||
| 541 | if (defined($opt{$o})) { | ||
| 542 | $o = $opt{$o}; | ||
| 543 | $retval = "$retval$o"; | ||
| 544 | } elsif (defined($opt{$var})) { | ||
| 545 | $o = $opt{$var}; | ||
| 546 | $retval = "$retval$o"; | ||
| 547 | } else { | ||
| 548 | $retval = "$retval\$\{$var\}"; | ||
| 549 | } | ||
| 550 | |||
| 551 | $option = $end; | ||
| 552 | } | ||
| 553 | |||
| 554 | $retval = "$retval$option"; | ||
| 555 | |||
| 556 | $retval =~ s/^ //; | ||
| 557 | |||
| 558 | return $retval; | ||
| 559 | } | ||
| 560 | |||
| 561 | sub eval_option { | ||
| 562 | my ($option, $i) = @_; | ||
| 563 | |||
| 564 | my $prev = ""; | ||
| 565 | |||
| 566 | # Since an option can evaluate to another option, | ||
| 567 | # keep iterating until we do not evaluate any more | ||
| 568 | # options. | ||
| 569 | my $r = 0; | ||
| 570 | while ($prev ne $option) { | ||
| 571 | # Check for recursive evaluations. | ||
| 572 | # 100 deep should be more than enough. | ||
| 573 | if ($r++ > 100) { | ||
| 574 | die "Over 100 evaluations accurred with $option\n" . | ||
| 575 | "Check for recursive variables\n"; | ||
| 576 | } | ||
| 577 | $prev = $option; | ||
| 578 | $option = __eval_option($option, $i); | ||
| 579 | } | ||
| 580 | |||
| 581 | return $option; | ||
| 582 | } | ||
| 583 | |||
| 476 | sub _logit { | 584 | sub _logit { |
| 477 | if (defined($opt{"LOG_FILE"})) { | 585 | if (defined($opt{"LOG_FILE"})) { |
| 478 | open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; | 586 | open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; |
| @@ -617,9 +725,15 @@ sub fail { | |||
| 617 | end_monitor; | 725 | end_monitor; |
| 618 | } | 726 | } |
| 619 | 727 | ||
| 728 | my $name = ""; | ||
| 729 | |||
| 730 | if (defined($test_name)) { | ||
| 731 | $name = " ($test_name)"; | ||
| 732 | } | ||
| 733 | |||
| 620 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | 734 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; |
| 621 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | 735 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; |
| 622 | doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n"; | 736 | doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n"; |
| 623 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | 737 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; |
| 624 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | 738 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; |
| 625 | 739 | ||
| @@ -836,17 +950,35 @@ sub monitor { | |||
| 836 | my $failure_start; | 950 | my $failure_start; |
| 837 | my $monitor_start = time; | 951 | my $monitor_start = time; |
| 838 | my $done = 0; | 952 | my $done = 0; |
| 953 | my $version_found = 0; | ||
| 839 | 954 | ||
| 840 | while (!$done) { | 955 | while (!$done) { |
| 841 | 956 | ||
| 842 | if ($booted) { | 957 | if ($bug && defined($stop_after_failure) && |
| 958 | $stop_after_failure >= 0) { | ||
| 959 | my $time = $stop_after_failure - (time - $failure_start); | ||
| 960 | $line = wait_for_input($monitor_fp, $time); | ||
| 961 | if (!defined($line)) { | ||
| 962 | doprint "bug timed out after $booted_timeout seconds\n"; | ||
| 963 | doprint "Test forced to stop after $stop_after_failure seconds after failure\n"; | ||
| 964 | last; | ||
| 965 | } | ||
| 966 | } elsif ($booted) { | ||
| 843 | $line = wait_for_input($monitor_fp, $booted_timeout); | 967 | $line = wait_for_input($monitor_fp, $booted_timeout); |
| 968 | if (!defined($line)) { | ||
| 969 | my $s = $booted_timeout == 1 ? "" : "s"; | ||
| 970 | doprint "Successful boot found: break after $booted_timeout second$s\n"; | ||
| 971 | last; | ||
| 972 | } | ||
| 844 | } else { | 973 | } else { |
| 845 | $line = wait_for_input($monitor_fp); | 974 | $line = wait_for_input($monitor_fp); |
| 975 | if (!defined($line)) { | ||
| 976 | my $s = $timeout == 1 ? "" : "s"; | ||
| 977 | doprint "Timed out after $timeout second$s\n"; | ||
| 978 | last; | ||
| 979 | } | ||
| 846 | } | 980 | } |
| 847 | 981 | ||
| 848 | last if (!defined($line)); | ||
| 849 | |||
| 850 | doprint $line; | 982 | doprint $line; |
| 851 | print DMESG $line; | 983 | print DMESG $line; |
| 852 | 984 | ||
| @@ -896,6 +1028,22 @@ sub monitor { | |||
| 896 | $bug = 1; | 1028 | $bug = 1; |
| 897 | } | 1029 | } |
| 898 | 1030 | ||
| 1031 | # Detect triple faults by testing the banner | ||
| 1032 | if ($full_line =~ /\bLinux version (\S+).*\n/) { | ||
| 1033 | if ($1 eq $version) { | ||
| 1034 | $version_found = 1; | ||
| 1035 | } elsif ($version_found && $detect_triplefault) { | ||
| 1036 | # We already booted into the kernel we are testing, | ||
| 1037 | # but now we booted into another kernel? | ||
| 1038 | # Consider this a triple fault. | ||
| 1039 | doprint "Aleady booted in Linux kernel $version, but now\n"; | ||
| 1040 | doprint "we booted into Linux kernel $1.\n"; | ||
| 1041 | doprint "Assuming that this is a triple fault.\n"; | ||
| 1042 | doprint "To disable this: set DETECT_TRIPLE_FAULT to 0\n"; | ||
| 1043 | last; | ||
| 1044 | } | ||
| 1045 | } | ||
| 1046 | |||
| 899 | if ($line =~ /\n/) { | 1047 | if ($line =~ /\n/) { |
| 900 | $full_line = ""; | 1048 | $full_line = ""; |
| 901 | } | 1049 | } |
| @@ -923,6 +1071,16 @@ sub monitor { | |||
| 923 | return 1; | 1071 | return 1; |
| 924 | } | 1072 | } |
| 925 | 1073 | ||
| 1074 | sub do_post_install { | ||
| 1075 | |||
| 1076 | return if (!defined($post_install)); | ||
| 1077 | |||
| 1078 | my $cp_post_install = $post_install; | ||
| 1079 | $cp_post_install =~ s/\$KERNEL_VERSION/$version/g; | ||
| 1080 | run_command "$cp_post_install" or | ||
| 1081 | dodie "Failed to run post install"; | ||
| 1082 | } | ||
| 1083 | |||
| 926 | sub install { | 1084 | sub install { |
| 927 | 1085 | ||
| 928 | run_scp "$outputdir/$build_target", "$target_image" or | 1086 | run_scp "$outputdir/$build_target", "$target_image" or |
| @@ -942,6 +1100,7 @@ sub install { | |||
| 942 | close(IN); | 1100 | close(IN); |
| 943 | 1101 | ||
| 944 | if (!$install_mods) { | 1102 | if (!$install_mods) { |
| 1103 | do_post_install; | ||
| 945 | doprint "No modules needed\n"; | 1104 | doprint "No modules needed\n"; |
| 946 | return; | 1105 | return; |
| 947 | } | 1106 | } |
| @@ -964,17 +1123,29 @@ sub install { | |||
| 964 | 1123 | ||
| 965 | unlink "$tmpdir/$modtar"; | 1124 | unlink "$tmpdir/$modtar"; |
| 966 | 1125 | ||
| 967 | run_ssh "'(cd / && tar xf /tmp/$modtar)'" or | 1126 | run_ssh "'(cd / && tar xjf /tmp/$modtar)'" or |
| 968 | dodie "failed to tar modules"; | 1127 | dodie "failed to tar modules"; |
| 969 | 1128 | ||
| 970 | run_ssh "rm -f /tmp/$modtar"; | 1129 | run_ssh "rm -f /tmp/$modtar"; |
| 971 | 1130 | ||
| 972 | return if (!defined($post_install)); | 1131 | do_post_install; |
| 1132 | } | ||
| 973 | 1133 | ||
| 974 | my $cp_post_install = $post_install; | 1134 | sub get_version { |
| 975 | $cp_post_install =~ s/\$KERNEL_VERSION/$version/g; | 1135 | # get the release name |
| 976 | run_command "$cp_post_install" or | 1136 | doprint "$make kernelrelease ... "; |
| 977 | dodie "Failed to run post install"; | 1137 | $version = `$make kernelrelease | tail -1`; |
| 1138 | chomp($version); | ||
| 1139 | doprint "$version\n"; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | sub start_monitor_and_boot { | ||
| 1143 | get_grub_index; | ||
| 1144 | get_version; | ||
| 1145 | install; | ||
| 1146 | |||
| 1147 | start_monitor; | ||
| 1148 | return monitor; | ||
| 978 | } | 1149 | } |
| 979 | 1150 | ||
| 980 | sub check_buildlog { | 1151 | sub check_buildlog { |
| @@ -1009,24 +1180,84 @@ sub check_buildlog { | |||
| 1009 | return 1; | 1180 | return 1; |
| 1010 | } | 1181 | } |
| 1011 | 1182 | ||
| 1183 | sub apply_min_config { | ||
| 1184 | my $outconfig = "$output_config.new"; | ||
| 1185 | |||
| 1186 | # Read the config file and remove anything that | ||
| 1187 | # is in the force_config hash (from minconfig and others) | ||
| 1188 | # then add the force config back. | ||
| 1189 | |||
| 1190 | doprint "Applying minimum configurations into $output_config.new\n"; | ||
| 1191 | |||
| 1192 | open (OUT, ">$outconfig") or | ||
| 1193 | dodie "Can't create $outconfig"; | ||
| 1194 | |||
| 1195 | if (-f $output_config) { | ||
| 1196 | open (IN, $output_config) or | ||
| 1197 | dodie "Failed to open $output_config"; | ||
| 1198 | while (<IN>) { | ||
| 1199 | if (/^(# )?(CONFIG_[^\s=]*)/) { | ||
| 1200 | next if (defined($force_config{$2})); | ||
| 1201 | } | ||
| 1202 | print OUT; | ||
| 1203 | } | ||
| 1204 | close IN; | ||
| 1205 | } | ||
| 1206 | foreach my $config (keys %force_config) { | ||
| 1207 | print OUT "$force_config{$config}\n"; | ||
| 1208 | } | ||
| 1209 | close OUT; | ||
| 1210 | |||
| 1211 | run_command "mv $outconfig $output_config"; | ||
| 1212 | } | ||
| 1213 | |||
| 1012 | sub make_oldconfig { | 1214 | sub make_oldconfig { |
| 1013 | my ($defconfig) = @_; | ||
| 1014 | 1215 | ||
| 1015 | if (!run_command "$defconfig $make oldnoconfig") { | 1216 | my @force_list = keys %force_config; |
| 1217 | |||
| 1218 | if ($#force_list >= 0) { | ||
| 1219 | apply_min_config; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | if (!run_command "$make oldnoconfig") { | ||
| 1016 | # Perhaps oldnoconfig doesn't exist in this version of the kernel | 1223 | # Perhaps oldnoconfig doesn't exist in this version of the kernel |
| 1017 | # try a yes '' | oldconfig | 1224 | # try a yes '' | oldconfig |
| 1018 | doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; | 1225 | doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; |
| 1019 | run_command "yes '' | $defconfig $make oldconfig" or | 1226 | run_command "yes '' | $make oldconfig" or |
| 1020 | dodie "failed make config oldconfig"; | 1227 | dodie "failed make config oldconfig"; |
| 1021 | } | 1228 | } |
| 1022 | } | 1229 | } |
| 1023 | 1230 | ||
| 1231 | # read a config file and use this to force new configs. | ||
| 1232 | sub load_force_config { | ||
| 1233 | my ($config) = @_; | ||
| 1234 | |||
| 1235 | open(IN, $config) or | ||
| 1236 | dodie "failed to read $config"; | ||
| 1237 | while (<IN>) { | ||
| 1238 | chomp; | ||
| 1239 | if (/^(CONFIG[^\s=]*)(\s*=.*)/) { | ||
| 1240 | $force_config{$1} = $_; | ||
| 1241 | } elsif (/^# (CONFIG_\S*) is not set/) { | ||
| 1242 | $force_config{$1} = $_; | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | close IN; | ||
| 1246 | } | ||
| 1247 | |||
| 1024 | sub build { | 1248 | sub build { |
| 1025 | my ($type) = @_; | 1249 | my ($type) = @_; |
| 1026 | my $defconfig = ""; | ||
| 1027 | 1250 | ||
| 1028 | unlink $buildlog; | 1251 | unlink $buildlog; |
| 1029 | 1252 | ||
| 1253 | if (defined($pre_build)) { | ||
| 1254 | my $ret = run_command $pre_build; | ||
| 1255 | if (!$ret && defined($pre_build_die) && | ||
| 1256 | $pre_build_die) { | ||
| 1257 | dodie "failed to pre_build\n"; | ||
| 1258 | } | ||
| 1259 | } | ||
| 1260 | |||
| 1030 | if ($type =~ /^useconfig:(.*)/) { | 1261 | if ($type =~ /^useconfig:(.*)/) { |
| 1031 | run_command "cp $1 $output_config" or | 1262 | run_command "cp $1 $output_config" or |
| 1032 | dodie "could not copy $1 to .config"; | 1263 | dodie "could not copy $1 to .config"; |
| @@ -1063,24 +1294,33 @@ sub build { | |||
| 1063 | close(OUT); | 1294 | close(OUT); |
| 1064 | 1295 | ||
| 1065 | if (defined($minconfig)) { | 1296 | if (defined($minconfig)) { |
| 1066 | $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; | 1297 | load_force_config($minconfig); |
| 1067 | } | 1298 | } |
| 1068 | 1299 | ||
| 1069 | if ($type eq "oldnoconfig") { | 1300 | if ($type ne "oldnoconfig") { |
| 1070 | make_oldconfig $defconfig; | 1301 | run_command "$make $type" or |
| 1071 | } else { | ||
| 1072 | run_command "$defconfig $make $type" or | ||
| 1073 | dodie "failed make config"; | 1302 | dodie "failed make config"; |
| 1074 | } | 1303 | } |
| 1304 | # Run old config regardless, to enforce min configurations | ||
| 1305 | make_oldconfig; | ||
| 1075 | 1306 | ||
| 1076 | $redirect = "$buildlog"; | 1307 | $redirect = "$buildlog"; |
| 1077 | if (!run_command "$make $build_options") { | 1308 | my $build_ret = run_command "$make $build_options"; |
| 1078 | undef $redirect; | 1309 | undef $redirect; |
| 1310 | |||
| 1311 | if (defined($post_build)) { | ||
| 1312 | my $ret = run_command $post_build; | ||
| 1313 | if (!$ret && defined($post_build_die) && | ||
| 1314 | $post_build_die) { | ||
| 1315 | dodie "failed to post_build\n"; | ||
| 1316 | } | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | if (!$build_ret) { | ||
| 1079 | # bisect may need this to pass | 1320 | # bisect may need this to pass |
| 1080 | return 0 if ($in_bisect); | 1321 | return 0 if ($in_bisect); |
| 1081 | fail "failed build" and return 0; | 1322 | fail "failed build" and return 0; |
| 1082 | } | 1323 | } |
| 1083 | undef $redirect; | ||
| 1084 | 1324 | ||
| 1085 | return 1; | 1325 | return 1; |
| 1086 | } | 1326 | } |
| @@ -1102,9 +1342,15 @@ sub success { | |||
| 1102 | 1342 | ||
| 1103 | $successes++; | 1343 | $successes++; |
| 1104 | 1344 | ||
| 1345 | my $name = ""; | ||
| 1346 | |||
| 1347 | if (defined($test_name)) { | ||
| 1348 | $name = " ($test_name)"; | ||
| 1349 | } | ||
| 1350 | |||
| 1105 | doprint "\n\n*******************************************\n"; | 1351 | doprint "\n\n*******************************************\n"; |
| 1106 | doprint "*******************************************\n"; | 1352 | doprint "*******************************************\n"; |
| 1107 | doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n"; | 1353 | doprint "KTEST RESULT: TEST $i$name SUCCESS!!!! **\n"; |
| 1108 | doprint "*******************************************\n"; | 1354 | doprint "*******************************************\n"; |
| 1109 | doprint "*******************************************\n"; | 1355 | doprint "*******************************************\n"; |
| 1110 | 1356 | ||
| @@ -1117,14 +1363,6 @@ sub success { | |||
| 1117 | } | 1363 | } |
| 1118 | } | 1364 | } |
| 1119 | 1365 | ||
| 1120 | sub get_version { | ||
| 1121 | # get the release name | ||
| 1122 | doprint "$make kernelrelease ... "; | ||
| 1123 | $version = `$make kernelrelease | tail -1`; | ||
| 1124 | chomp($version); | ||
| 1125 | doprint "$version\n"; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | sub answer_bisect { | 1366 | sub answer_bisect { |
| 1129 | for (;;) { | 1367 | for (;;) { |
| 1130 | doprint "Pass or fail? [p/f]"; | 1368 | doprint "Pass or fail? [p/f]"; |
| @@ -1289,12 +1527,7 @@ sub run_bisect_test { | |||
| 1289 | dodie "Failed on build" if $failed; | 1527 | dodie "Failed on build" if $failed; |
| 1290 | 1528 | ||
| 1291 | # Now boot the box | 1529 | # Now boot the box |
| 1292 | get_grub_index; | 1530 | start_monitor_and_boot or $failed = 1; |
| 1293 | get_version; | ||
| 1294 | install; | ||
| 1295 | |||
| 1296 | start_monitor; | ||
| 1297 | monitor or $failed = 1; | ||
| 1298 | 1531 | ||
| 1299 | if ($type ne "boot") { | 1532 | if ($type ne "boot") { |
| 1300 | if ($failed && $bisect_skip) { | 1533 | if ($failed && $bisect_skip) { |
| @@ -1473,21 +1706,27 @@ my %null_config; | |||
| 1473 | 1706 | ||
| 1474 | my %dependency; | 1707 | my %dependency; |
| 1475 | 1708 | ||
| 1476 | sub process_config_ignore { | 1709 | sub assign_configs { |
| 1477 | my ($config) = @_; | 1710 | my ($hash, $config) = @_; |
| 1478 | 1711 | ||
| 1479 | open (IN, $config) | 1712 | open (IN, $config) |
| 1480 | or dodie "Failed to read $config"; | 1713 | or dodie "Failed to read $config"; |
| 1481 | 1714 | ||
| 1482 | while (<IN>) { | 1715 | while (<IN>) { |
| 1483 | if (/^((CONFIG\S*)=.*)/) { | 1716 | if (/^((CONFIG\S*)=.*)/) { |
| 1484 | $config_ignore{$2} = $1; | 1717 | ${$hash}{$2} = $1; |
| 1485 | } | 1718 | } |
| 1486 | } | 1719 | } |
| 1487 | 1720 | ||
| 1488 | close(IN); | 1721 | close(IN); |
| 1489 | } | 1722 | } |
| 1490 | 1723 | ||
| 1724 | sub process_config_ignore { | ||
| 1725 | my ($config) = @_; | ||
| 1726 | |||
| 1727 | assign_configs \%config_ignore, $config; | ||
| 1728 | } | ||
| 1729 | |||
| 1491 | sub read_current_config { | 1730 | sub read_current_config { |
| 1492 | my ($config_ref) = @_; | 1731 | my ($config_ref) = @_; |
| 1493 | 1732 | ||
| @@ -1546,7 +1785,7 @@ sub create_config { | |||
| 1546 | close(OUT); | 1785 | close(OUT); |
| 1547 | 1786 | ||
| 1548 | # exit; | 1787 | # exit; |
| 1549 | make_oldconfig ""; | 1788 | make_oldconfig; |
| 1550 | } | 1789 | } |
| 1551 | 1790 | ||
| 1552 | sub compare_configs { | 1791 | sub compare_configs { |
| @@ -1718,6 +1957,10 @@ sub config_bisect { | |||
| 1718 | 1957 | ||
| 1719 | my $tmpconfig = "$tmpdir/use_config"; | 1958 | my $tmpconfig = "$tmpdir/use_config"; |
| 1720 | 1959 | ||
| 1960 | if (defined($config_bisect_good)) { | ||
| 1961 | process_config_ignore $config_bisect_good; | ||
| 1962 | } | ||
| 1963 | |||
| 1721 | # Make the file with the bad config and the min config | 1964 | # Make the file with the bad config and the min config |
| 1722 | if (defined($minconfig)) { | 1965 | if (defined($minconfig)) { |
| 1723 | # read the min config for things to ignore | 1966 | # read the min config for things to ignore |
| @@ -1727,15 +1970,8 @@ sub config_bisect { | |||
| 1727 | unlink $tmpconfig; | 1970 | unlink $tmpconfig; |
| 1728 | } | 1971 | } |
| 1729 | 1972 | ||
| 1730 | # Add other configs | ||
| 1731 | if (defined($addconfig)) { | ||
| 1732 | run_command "cat $addconfig >> $tmpconfig" or | ||
| 1733 | dodie "failed to append $addconfig"; | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | my $defconfig = ""; | ||
| 1737 | if (-f $tmpconfig) { | 1973 | if (-f $tmpconfig) { |
| 1738 | $defconfig = "KCONFIG_ALLCONFIG=$tmpconfig"; | 1974 | load_force_config($tmpconfig); |
| 1739 | process_config_ignore $tmpconfig; | 1975 | process_config_ignore $tmpconfig; |
| 1740 | } | 1976 | } |
| 1741 | 1977 | ||
| @@ -1755,8 +1991,8 @@ sub config_bisect { | |||
| 1755 | } | 1991 | } |
| 1756 | close(IN); | 1992 | close(IN); |
| 1757 | 1993 | ||
| 1758 | # Now run oldconfig with the minconfig (and addconfigs) | 1994 | # Now run oldconfig with the minconfig |
| 1759 | make_oldconfig $defconfig; | 1995 | make_oldconfig; |
| 1760 | 1996 | ||
| 1761 | # check to see what we lost (or gained) | 1997 | # check to see what we lost (or gained) |
| 1762 | open (IN, $output_config) | 1998 | open (IN, $output_config) |
| @@ -1882,6 +2118,13 @@ sub patchcheck { | |||
| 1882 | @list = reverse @list; | 2118 | @list = reverse @list; |
| 1883 | 2119 | ||
| 1884 | my $save_clean = $noclean; | 2120 | my $save_clean = $noclean; |
| 2121 | my %ignored_warnings; | ||
| 2122 | |||
| 2123 | if (defined($ignore_warnings)) { | ||
| 2124 | foreach my $sha1 (split /\s+/, $ignore_warnings) { | ||
| 2125 | $ignored_warnings{$sha1} = 1; | ||
| 2126 | } | ||
| 2127 | } | ||
| 1885 | 2128 | ||
| 1886 | $in_patchcheck = 1; | 2129 | $in_patchcheck = 1; |
| 1887 | foreach my $item (@list) { | 2130 | foreach my $item (@list) { |
| @@ -1908,18 +2151,16 @@ sub patchcheck { | |||
| 1908 | build "oldconfig" or return 0; | 2151 | build "oldconfig" or return 0; |
| 1909 | } | 2152 | } |
| 1910 | 2153 | ||
| 1911 | check_buildlog $sha1 or return 0; | ||
| 1912 | 2154 | ||
| 1913 | next if ($type eq "build"); | 2155 | if (!defined($ignored_warnings{$sha1})) { |
| 2156 | check_buildlog $sha1 or return 0; | ||
| 2157 | } | ||
| 1914 | 2158 | ||
| 1915 | get_grub_index; | 2159 | next if ($type eq "build"); |
| 1916 | get_version; | ||
| 1917 | install; | ||
| 1918 | 2160 | ||
| 1919 | my $failed = 0; | 2161 | my $failed = 0; |
| 1920 | 2162 | ||
| 1921 | start_monitor; | 2163 | start_monitor_and_boot or $failed = 1; |
| 1922 | monitor or $failed = 1; | ||
| 1923 | 2164 | ||
| 1924 | if (!$failed && $type ne "boot"){ | 2165 | if (!$failed && $type ne "boot"){ |
| 1925 | do_run_test or $failed = 1; | 2166 | do_run_test or $failed = 1; |
| @@ -1936,24 +2177,505 @@ sub patchcheck { | |||
| 1936 | return 1; | 2177 | return 1; |
| 1937 | } | 2178 | } |
| 1938 | 2179 | ||
| 2180 | my %depends; | ||
| 2181 | my $iflevel = 0; | ||
| 2182 | my @ifdeps; | ||
| 2183 | |||
| 2184 | # prevent recursion | ||
| 2185 | my %read_kconfigs; | ||
| 2186 | |||
| 2187 | # taken from streamline_config.pl | ||
| 2188 | sub read_kconfig { | ||
| 2189 | my ($kconfig) = @_; | ||
| 2190 | |||
| 2191 | my $state = "NONE"; | ||
| 2192 | my $config; | ||
| 2193 | my @kconfigs; | ||
| 2194 | |||
| 2195 | my $cont = 0; | ||
| 2196 | my $line; | ||
| 2197 | |||
| 2198 | |||
| 2199 | if (! -f $kconfig) { | ||
| 2200 | doprint "file $kconfig does not exist, skipping\n"; | ||
| 2201 | return; | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | open(KIN, "$kconfig") | ||
| 2205 | or die "Can't open $kconfig"; | ||
| 2206 | while (<KIN>) { | ||
| 2207 | chomp; | ||
| 2208 | |||
| 2209 | # Make sure that lines ending with \ continue | ||
| 2210 | if ($cont) { | ||
| 2211 | $_ = $line . " " . $_; | ||
| 2212 | } | ||
| 2213 | |||
| 2214 | if (s/\\$//) { | ||
| 2215 | $cont = 1; | ||
| 2216 | $line = $_; | ||
| 2217 | next; | ||
| 2218 | } | ||
| 2219 | |||
| 2220 | $cont = 0; | ||
| 2221 | |||
| 2222 | # collect any Kconfig sources | ||
| 2223 | if (/^source\s*"(.*)"/) { | ||
| 2224 | $kconfigs[$#kconfigs+1] = $1; | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | # configs found | ||
| 2228 | if (/^\s*(menu)?config\s+(\S+)\s*$/) { | ||
| 2229 | $state = "NEW"; | ||
| 2230 | $config = $2; | ||
| 2231 | |||
| 2232 | for (my $i = 0; $i < $iflevel; $i++) { | ||
| 2233 | if ($i) { | ||
| 2234 | $depends{$config} .= " " . $ifdeps[$i]; | ||
| 2235 | } else { | ||
| 2236 | $depends{$config} = $ifdeps[$i]; | ||
| 2237 | } | ||
| 2238 | $state = "DEP"; | ||
| 2239 | } | ||
| 2240 | |||
| 2241 | # collect the depends for the config | ||
| 2242 | } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) { | ||
| 2243 | |||
| 2244 | if (defined($depends{$1})) { | ||
| 2245 | $depends{$config} .= " " . $1; | ||
| 2246 | } else { | ||
| 2247 | $depends{$config} = $1; | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | # Get the configs that select this config | ||
| 2251 | } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) { | ||
| 2252 | if (defined($depends{$1})) { | ||
| 2253 | $depends{$1} .= " " . $config; | ||
| 2254 | } else { | ||
| 2255 | $depends{$1} = $config; | ||
| 2256 | } | ||
| 2257 | |||
| 2258 | # Check for if statements | ||
| 2259 | } elsif (/^if\s+(.*\S)\s*$/) { | ||
| 2260 | my $deps = $1; | ||
| 2261 | # remove beginning and ending non text | ||
| 2262 | $deps =~ s/^[^a-zA-Z0-9_]*//; | ||
| 2263 | $deps =~ s/[^a-zA-Z0-9_]*$//; | ||
| 2264 | |||
| 2265 | my @deps = split /[^a-zA-Z0-9_]+/, $deps; | ||
| 2266 | |||
| 2267 | $ifdeps[$iflevel++] = join ':', @deps; | ||
| 2268 | |||
| 2269 | } elsif (/^endif/) { | ||
| 2270 | |||
| 2271 | $iflevel-- if ($iflevel); | ||
| 2272 | |||
| 2273 | # stop on "help" | ||
| 2274 | } elsif (/^\s*help\s*$/) { | ||
| 2275 | $state = "NONE"; | ||
| 2276 | } | ||
| 2277 | } | ||
| 2278 | close(KIN); | ||
| 2279 | |||
| 2280 | # read in any configs that were found. | ||
| 2281 | foreach $kconfig (@kconfigs) { | ||
| 2282 | if (!defined($read_kconfigs{$kconfig})) { | ||
| 2283 | $read_kconfigs{$kconfig} = 1; | ||
| 2284 | read_kconfig("$builddir/$kconfig"); | ||
| 2285 | } | ||
| 2286 | } | ||
| 2287 | } | ||
| 2288 | |||
| 2289 | sub read_depends { | ||
| 2290 | # find out which arch this is by the kconfig file | ||
| 2291 | open (IN, $output_config) | ||
| 2292 | or dodie "Failed to read $output_config"; | ||
| 2293 | my $arch; | ||
| 2294 | while (<IN>) { | ||
| 2295 | if (m,Linux/(\S+)\s+\S+\s+Kernel Configuration,) { | ||
| 2296 | $arch = $1; | ||
| 2297 | last; | ||
| 2298 | } | ||
| 2299 | } | ||
| 2300 | close IN; | ||
| 2301 | |||
| 2302 | if (!defined($arch)) { | ||
| 2303 | doprint "Could not find arch from config file\n"; | ||
| 2304 | doprint "no dependencies used\n"; | ||
| 2305 | return; | ||
| 2306 | } | ||
| 2307 | |||
| 2308 | # arch is really the subarch, we need to know | ||
| 2309 | # what directory to look at. | ||
| 2310 | if ($arch eq "i386" || $arch eq "x86_64") { | ||
| 2311 | $arch = "x86"; | ||
| 2312 | } elsif ($arch =~ /^tile/) { | ||
| 2313 | $arch = "tile"; | ||
| 2314 | } | ||
| 2315 | |||
| 2316 | my $kconfig = "$builddir/arch/$arch/Kconfig"; | ||
| 2317 | |||
| 2318 | if (! -f $kconfig && $arch =~ /\d$/) { | ||
| 2319 | my $orig = $arch; | ||
| 2320 | # some subarchs have numbers, truncate them | ||
| 2321 | $arch =~ s/\d*$//; | ||
| 2322 | $kconfig = "$builddir/arch/$arch/Kconfig"; | ||
| 2323 | if (! -f $kconfig) { | ||
| 2324 | doprint "No idea what arch dir $orig is for\n"; | ||
| 2325 | doprint "no dependencies used\n"; | ||
| 2326 | return; | ||
| 2327 | } | ||
| 2328 | } | ||
| 2329 | |||
| 2330 | read_kconfig($kconfig); | ||
| 2331 | } | ||
| 2332 | |||
| 2333 | sub read_config_list { | ||
| 2334 | my ($config) = @_; | ||
| 2335 | |||
| 2336 | open (IN, $config) | ||
| 2337 | or dodie "Failed to read $config"; | ||
| 2338 | |||
| 2339 | while (<IN>) { | ||
| 2340 | if (/^((CONFIG\S*)=.*)/) { | ||
| 2341 | if (!defined($config_ignore{$2})) { | ||
| 2342 | $config_list{$2} = $1; | ||
| 2343 | } | ||
| 2344 | } | ||
| 2345 | } | ||
| 2346 | |||
| 2347 | close(IN); | ||
| 2348 | } | ||
| 2349 | |||
| 2350 | sub read_output_config { | ||
| 2351 | my ($config) = @_; | ||
| 2352 | |||
| 2353 | assign_configs \%config_ignore, $config; | ||
| 2354 | } | ||
| 2355 | |||
| 2356 | sub make_new_config { | ||
| 2357 | my @configs = @_; | ||
| 2358 | |||
| 2359 | open (OUT, ">$output_config") | ||
| 2360 | or dodie "Failed to write $output_config"; | ||
| 2361 | |||
| 2362 | foreach my $config (@configs) { | ||
| 2363 | print OUT "$config\n"; | ||
| 2364 | } | ||
| 2365 | close OUT; | ||
| 2366 | } | ||
| 2367 | |||
| 2368 | sub get_depends { | ||
| 2369 | my ($dep) = @_; | ||
| 2370 | |||
| 2371 | my $kconfig = $dep; | ||
| 2372 | $kconfig =~ s/CONFIG_//; | ||
| 2373 | |||
| 2374 | $dep = $depends{"$kconfig"}; | ||
| 2375 | |||
| 2376 | # the dep string we have saves the dependencies as they | ||
| 2377 | # were found, including expressions like ! && ||. We | ||
| 2378 | # want to split this out into just an array of configs. | ||
| 2379 | |||
| 2380 | my $valid = "A-Za-z_0-9"; | ||
| 2381 | |||
| 2382 | my @configs; | ||
| 2383 | |||
| 2384 | while ($dep =~ /[$valid]/) { | ||
| 2385 | |||
| 2386 | if ($dep =~ /^[^$valid]*([$valid]+)/) { | ||
| 2387 | my $conf = "CONFIG_" . $1; | ||
| 2388 | |||
| 2389 | $configs[$#configs + 1] = $conf; | ||
| 2390 | |||
| 2391 | $dep =~ s/^[^$valid]*[$valid]+//; | ||
| 2392 | } else { | ||
| 2393 | die "this should never happen"; | ||
| 2394 | } | ||
| 2395 | } | ||
| 2396 | |||
| 2397 | return @configs; | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | my %min_configs; | ||
| 2401 | my %keep_configs; | ||
| 2402 | my %save_configs; | ||
| 2403 | my %processed_configs; | ||
| 2404 | my %nochange_config; | ||
| 2405 | |||
| 2406 | sub test_this_config { | ||
| 2407 | my ($config) = @_; | ||
| 2408 | |||
| 2409 | my $found; | ||
| 2410 | |||
| 2411 | # if we already processed this config, skip it | ||
| 2412 | if (defined($processed_configs{$config})) { | ||
| 2413 | return undef; | ||
| 2414 | } | ||
| 2415 | $processed_configs{$config} = 1; | ||
| 2416 | |||
| 2417 | # if this config failed during this round, skip it | ||
| 2418 | if (defined($nochange_config{$config})) { | ||
| 2419 | return undef; | ||
| 2420 | } | ||
| 2421 | |||
| 2422 | my $kconfig = $config; | ||
| 2423 | $kconfig =~ s/CONFIG_//; | ||
| 2424 | |||
| 2425 | # Test dependencies first | ||
| 2426 | if (defined($depends{"$kconfig"})) { | ||
| 2427 | my @parents = get_depends $config; | ||
| 2428 | foreach my $parent (@parents) { | ||
| 2429 | # if the parent is in the min config, check it first | ||
| 2430 | next if (!defined($min_configs{$parent})); | ||
| 2431 | $found = test_this_config($parent); | ||
| 2432 | if (defined($found)) { | ||
| 2433 | return $found; | ||
| 2434 | } | ||
| 2435 | } | ||
| 2436 | } | ||
| 2437 | |||
| 2438 | # Remove this config from the list of configs | ||
| 2439 | # do a make oldnoconfig and then read the resulting | ||
| 2440 | # .config to make sure it is missing the config that | ||
| 2441 | # we had before | ||
| 2442 | my %configs = %min_configs; | ||
| 2443 | delete $configs{$config}; | ||
| 2444 | make_new_config ((values %configs), (values %keep_configs)); | ||
| 2445 | make_oldconfig; | ||
| 2446 | undef %configs; | ||
| 2447 | assign_configs \%configs, $output_config; | ||
| 2448 | |||
| 2449 | return $config if (!defined($configs{$config})); | ||
| 2450 | |||
| 2451 | doprint "disabling config $config did not change .config\n"; | ||
| 2452 | |||
| 2453 | $nochange_config{$config} = 1; | ||
| 2454 | |||
| 2455 | return undef; | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | sub make_min_config { | ||
| 2459 | my ($i) = @_; | ||
| 2460 | |||
| 2461 | if (!defined($output_minconfig)) { | ||
| 2462 | fail "OUTPUT_MIN_CONFIG not defined" and return; | ||
| 2463 | } | ||
| 2464 | |||
| 2465 | # If output_minconfig exists, and the start_minconfig | ||
| 2466 | # came from min_config, than ask if we should use | ||
| 2467 | # that instead. | ||
| 2468 | if (-f $output_minconfig && !$start_minconfig_defined) { | ||
| 2469 | print "$output_minconfig exists\n"; | ||
| 2470 | if (read_yn " Use it as minconfig?") { | ||
| 2471 | $start_minconfig = $output_minconfig; | ||
| 2472 | } | ||
| 2473 | } | ||
| 2474 | |||
| 2475 | if (!defined($start_minconfig)) { | ||
| 2476 | fail "START_MIN_CONFIG or MIN_CONFIG not defined" and return; | ||
| 2477 | } | ||
| 2478 | |||
| 2479 | my $temp_config = "$tmpdir/temp_config"; | ||
| 2480 | |||
| 2481 | # First things first. We build an allnoconfig to find | ||
| 2482 | # out what the defaults are that we can't touch. | ||
| 2483 | # Some are selections, but we really can't handle selections. | ||
| 2484 | |||
| 2485 | my $save_minconfig = $minconfig; | ||
| 2486 | undef $minconfig; | ||
| 2487 | |||
| 2488 | run_command "$make allnoconfig" or return 0; | ||
| 2489 | |||
| 2490 | read_depends; | ||
| 2491 | |||
| 2492 | process_config_ignore $output_config; | ||
| 2493 | |||
| 2494 | undef %save_configs; | ||
| 2495 | undef %min_configs; | ||
| 2496 | |||
| 2497 | if (defined($ignore_config)) { | ||
| 2498 | # make sure the file exists | ||
| 2499 | `touch $ignore_config`; | ||
| 2500 | assign_configs \%save_configs, $ignore_config; | ||
| 2501 | } | ||
| 2502 | |||
| 2503 | %keep_configs = %save_configs; | ||
| 2504 | |||
| 2505 | doprint "Load initial configs from $start_minconfig\n"; | ||
| 2506 | |||
| 2507 | # Look at the current min configs, and save off all the | ||
| 2508 | # ones that were set via the allnoconfig | ||
| 2509 | assign_configs \%min_configs, $start_minconfig; | ||
| 2510 | |||
| 2511 | my @config_keys = keys %min_configs; | ||
| 2512 | |||
| 2513 | # Remove anything that was set by the make allnoconfig | ||
| 2514 | # we shouldn't need them as they get set for us anyway. | ||
| 2515 | foreach my $config (@config_keys) { | ||
| 2516 | # Remove anything in the ignore_config | ||
| 2517 | if (defined($keep_configs{$config})) { | ||
| 2518 | my $file = $ignore_config; | ||
| 2519 | $file =~ s,.*/(.*?)$,$1,; | ||
| 2520 | doprint "$config set by $file ... ignored\n"; | ||
| 2521 | delete $min_configs{$config}; | ||
| 2522 | next; | ||
| 2523 | } | ||
| 2524 | # But make sure the settings are the same. If a min config | ||
| 2525 | # sets a selection, we do not want to get rid of it if | ||
| 2526 | # it is not the same as what we have. Just move it into | ||
| 2527 | # the keep configs. | ||
| 2528 | if (defined($config_ignore{$config})) { | ||
| 2529 | if ($config_ignore{$config} ne $min_configs{$config}) { | ||
| 2530 | doprint "$config is in allnoconfig as '$config_ignore{$config}'"; | ||
| 2531 | doprint " but it is '$min_configs{$config}' in minconfig .. keeping\n"; | ||
| 2532 | $keep_configs{$config} = $min_configs{$config}; | ||
| 2533 | } else { | ||
| 2534 | doprint "$config set by allnoconfig ... ignored\n"; | ||
| 2535 | } | ||
| 2536 | delete $min_configs{$config}; | ||
| 2537 | } | ||
| 2538 | } | ||
| 2539 | |||
| 2540 | my $done = 0; | ||
| 2541 | my $take_two = 0; | ||
| 2542 | |||
| 2543 | while (!$done) { | ||
| 2544 | |||
| 2545 | my $config; | ||
| 2546 | my $found; | ||
| 2547 | |||
| 2548 | # Now disable each config one by one and do a make oldconfig | ||
| 2549 | # till we find a config that changes our list. | ||
| 2550 | |||
| 2551 | # Put configs that did not modify the config at the end. | ||
| 2552 | my @test_configs = keys %min_configs; | ||
| 2553 | my $reset = 1; | ||
| 2554 | for (my $i = 0; $i < $#test_configs; $i++) { | ||
| 2555 | if (!defined($nochange_config{$test_configs[0]})) { | ||
| 2556 | $reset = 0; | ||
| 2557 | last; | ||
| 2558 | } | ||
| 2559 | # This config didn't change the .config last time. | ||
| 2560 | # Place it at the end | ||
| 2561 | my $config = shift @test_configs; | ||
| 2562 | push @test_configs, $config; | ||
| 2563 | } | ||
| 2564 | |||
| 2565 | # if every test config has failed to modify the .config file | ||
| 2566 | # in the past, then reset and start over. | ||
| 2567 | if ($reset) { | ||
| 2568 | undef %nochange_config; | ||
| 2569 | } | ||
| 2570 | |||
| 2571 | undef %processed_configs; | ||
| 2572 | |||
| 2573 | foreach my $config (@test_configs) { | ||
| 2574 | |||
| 2575 | $found = test_this_config $config; | ||
| 2576 | |||
| 2577 | last if (defined($found)); | ||
| 2578 | |||
| 2579 | # oh well, try another config | ||
| 2580 | } | ||
| 2581 | |||
| 2582 | if (!defined($found)) { | ||
| 2583 | # we could have failed due to the nochange_config hash | ||
| 2584 | # reset and try again | ||
| 2585 | if (!$take_two) { | ||
| 2586 | undef %nochange_config; | ||
| 2587 | $take_two = 1; | ||
| 2588 | next; | ||
| 2589 | } | ||
| 2590 | doprint "No more configs found that we can disable\n"; | ||
| 2591 | $done = 1; | ||
| 2592 | last; | ||
| 2593 | } | ||
| 2594 | $take_two = 0; | ||
| 2595 | |||
| 2596 | $config = $found; | ||
| 2597 | |||
| 2598 | doprint "Test with $config disabled\n"; | ||
| 2599 | |||
| 2600 | # set in_bisect to keep build and monitor from dieing | ||
| 2601 | $in_bisect = 1; | ||
| 2602 | |||
| 2603 | my $failed = 0; | ||
| 2604 | build "oldconfig"; | ||
| 2605 | start_monitor_and_boot or $failed = 1; | ||
| 2606 | end_monitor; | ||
| 2607 | |||
| 2608 | $in_bisect = 0; | ||
| 2609 | |||
| 2610 | if ($failed) { | ||
| 2611 | doprint "$min_configs{$config} is needed to boot the box... keeping\n"; | ||
| 2612 | # this config is needed, add it to the ignore list. | ||
| 2613 | $keep_configs{$config} = $min_configs{$config}; | ||
| 2614 | $save_configs{$config} = $min_configs{$config}; | ||
| 2615 | delete $min_configs{$config}; | ||
| 2616 | |||
| 2617 | # update new ignore configs | ||
| 2618 | if (defined($ignore_config)) { | ||
| 2619 | open (OUT, ">$temp_config") | ||
| 2620 | or die "Can't write to $temp_config"; | ||
| 2621 | foreach my $config (keys %save_configs) { | ||
| 2622 | print OUT "$save_configs{$config}\n"; | ||
| 2623 | } | ||
| 2624 | close OUT; | ||
| 2625 | run_command "mv $temp_config $ignore_config" or | ||
| 2626 | dodie "failed to copy update to $ignore_config"; | ||
| 2627 | } | ||
| 2628 | |||
| 2629 | } else { | ||
| 2630 | # We booted without this config, remove it from the minconfigs. | ||
| 2631 | doprint "$config is not needed, disabling\n"; | ||
| 2632 | |||
| 2633 | delete $min_configs{$config}; | ||
| 2634 | |||
| 2635 | # Also disable anything that is not enabled in this config | ||
| 2636 | my %configs; | ||
| 2637 | assign_configs \%configs, $output_config; | ||
| 2638 | my @config_keys = keys %min_configs; | ||
| 2639 | foreach my $config (@config_keys) { | ||
| 2640 | if (!defined($configs{$config})) { | ||
| 2641 | doprint "$config is not set, disabling\n"; | ||
| 2642 | delete $min_configs{$config}; | ||
| 2643 | } | ||
| 2644 | } | ||
| 2645 | |||
| 2646 | # Save off all the current mandidory configs | ||
| 2647 | open (OUT, ">$temp_config") | ||
| 2648 | or die "Can't write to $temp_config"; | ||
| 2649 | foreach my $config (keys %keep_configs) { | ||
| 2650 | print OUT "$keep_configs{$config}\n"; | ||
| 2651 | } | ||
| 2652 | foreach my $config (keys %min_configs) { | ||
| 2653 | print OUT "$min_configs{$config}\n"; | ||
| 2654 | } | ||
| 2655 | close OUT; | ||
| 2656 | |||
| 2657 | run_command "mv $temp_config $output_minconfig" or | ||
| 2658 | dodie "failed to copy update to $output_minconfig"; | ||
| 2659 | } | ||
| 2660 | |||
| 2661 | doprint "Reboot and wait $sleep_time seconds\n"; | ||
| 2662 | reboot; | ||
| 2663 | start_monitor; | ||
| 2664 | wait_for_monitor $sleep_time; | ||
| 2665 | end_monitor; | ||
| 2666 | } | ||
| 2667 | |||
| 2668 | success $i; | ||
| 2669 | return 1; | ||
| 2670 | } | ||
| 2671 | |||
| 1939 | $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; | 2672 | $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; |
| 1940 | 2673 | ||
| 1941 | if ($#ARGV == 0) { | 2674 | if ($#ARGV == 0) { |
| 1942 | $ktest_config = $ARGV[0]; | 2675 | $ktest_config = $ARGV[0]; |
| 1943 | if (! -f $ktest_config) { | 2676 | if (! -f $ktest_config) { |
| 1944 | print "$ktest_config does not exist.\n"; | 2677 | print "$ktest_config does not exist.\n"; |
| 1945 | my $ans; | 2678 | if (!read_yn "Create it?") { |
| 1946 | for (;;) { | ||
| 1947 | print "Create it? [Y/n] "; | ||
| 1948 | $ans = <STDIN>; | ||
| 1949 | chomp $ans; | ||
| 1950 | if ($ans =~ /^\s*$/) { | ||
| 1951 | $ans = "y"; | ||
| 1952 | } | ||
| 1953 | last if ($ans =~ /^y$/i || $ans =~ /^n$/i); | ||
| 1954 | print "Please answer either 'y' or 'n'.\n"; | ||
| 1955 | } | ||
| 1956 | if ($ans !~ /^y$/i) { | ||
| 1957 | exit 0; | 2679 | exit 0; |
| 1958 | } | 2680 | } |
| 1959 | } | 2681 | } |
| @@ -1977,6 +2699,10 @@ EOF | |||
| 1977 | } | 2699 | } |
| 1978 | read_config $ktest_config; | 2700 | read_config $ktest_config; |
| 1979 | 2701 | ||
| 2702 | if (defined($opt{"LOG_FILE"})) { | ||
| 2703 | $opt{"LOG_FILE"} = eval_option($opt{"LOG_FILE"}, -1); | ||
| 2704 | } | ||
| 2705 | |||
| 1980 | # Append any configs entered in manually to the config file. | 2706 | # Append any configs entered in manually to the config file. |
| 1981 | my @new_configs = keys %entered_configs; | 2707 | my @new_configs = keys %entered_configs; |
| 1982 | if ($#new_configs >= 0) { | 2708 | if ($#new_configs >= 0) { |
| @@ -2045,70 +2771,13 @@ sub __set_test_option { | |||
| 2045 | return undef; | 2771 | return undef; |
| 2046 | } | 2772 | } |
| 2047 | 2773 | ||
| 2048 | sub eval_option { | ||
| 2049 | my ($option, $i) = @_; | ||
| 2050 | |||
| 2051 | # Add space to evaluate the character before $ | ||
| 2052 | $option = " $option"; | ||
| 2053 | my $retval = ""; | ||
| 2054 | |||
| 2055 | while ($option =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) { | ||
| 2056 | my $start = $1; | ||
| 2057 | my $var = $2; | ||
| 2058 | my $end = $3; | ||
| 2059 | |||
| 2060 | # Append beginning of line | ||
| 2061 | $retval = "$retval$start"; | ||
| 2062 | |||
| 2063 | # If the iteration option OPT[$i] exists, then use that. | ||
| 2064 | # otherwise see if the default OPT (without [$i]) exists. | ||
| 2065 | |||
| 2066 | my $o = "$var\[$i\]"; | ||
| 2067 | |||
| 2068 | if (defined($opt{$o})) { | ||
| 2069 | $o = $opt{$o}; | ||
| 2070 | $retval = "$retval$o"; | ||
| 2071 | } elsif (defined($opt{$var})) { | ||
| 2072 | $o = $opt{$var}; | ||
| 2073 | $retval = "$retval$o"; | ||
| 2074 | } else { | ||
| 2075 | $retval = "$retval\$\{$var\}"; | ||
| 2076 | } | ||
| 2077 | |||
| 2078 | $option = $end; | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | $retval = "$retval$option"; | ||
| 2082 | |||
| 2083 | $retval =~ s/^ //; | ||
| 2084 | |||
| 2085 | return $retval; | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | sub set_test_option { | 2774 | sub set_test_option { |
| 2089 | my ($name, $i) = @_; | 2775 | my ($name, $i) = @_; |
| 2090 | 2776 | ||
| 2091 | my $option = __set_test_option($name, $i); | 2777 | my $option = __set_test_option($name, $i); |
| 2092 | return $option if (!defined($option)); | 2778 | return $option if (!defined($option)); |
| 2093 | 2779 | ||
| 2094 | my $prev = ""; | 2780 | return eval_option($option, $i); |
| 2095 | |||
| 2096 | # Since an option can evaluate to another option, | ||
| 2097 | # keep iterating until we do not evaluate any more | ||
| 2098 | # options. | ||
| 2099 | my $r = 0; | ||
| 2100 | while ($prev ne $option) { | ||
| 2101 | # Check for recursive evaluations. | ||
| 2102 | # 100 deep should be more than enough. | ||
| 2103 | if ($r++ > 100) { | ||
| 2104 | die "Over 100 evaluations accurred with $name\n" . | ||
| 2105 | "Check for recursive variables\n"; | ||
| 2106 | } | ||
| 2107 | $prev = $option; | ||
| 2108 | $option = eval_option($option, $i); | ||
| 2109 | } | ||
| 2110 | |||
| 2111 | return $option; | ||
| 2112 | } | 2781 | } |
| 2113 | 2782 | ||
| 2114 | # First we need to do is the builds | 2783 | # First we need to do is the builds |
| @@ -2126,10 +2795,17 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
| 2126 | $test_type = set_test_option("TEST_TYPE", $i); | 2795 | $test_type = set_test_option("TEST_TYPE", $i); |
| 2127 | $build_type = set_test_option("BUILD_TYPE", $i); | 2796 | $build_type = set_test_option("BUILD_TYPE", $i); |
| 2128 | $build_options = set_test_option("BUILD_OPTIONS", $i); | 2797 | $build_options = set_test_option("BUILD_OPTIONS", $i); |
| 2798 | $pre_build = set_test_option("PRE_BUILD", $i); | ||
| 2799 | $post_build = set_test_option("POST_BUILD", $i); | ||
| 2800 | $pre_build_die = set_test_option("PRE_BUILD_DIE", $i); | ||
| 2801 | $post_build_die = set_test_option("POST_BUILD_DIE", $i); | ||
| 2129 | $power_cycle = set_test_option("POWER_CYCLE", $i); | 2802 | $power_cycle = set_test_option("POWER_CYCLE", $i); |
| 2130 | $reboot = set_test_option("REBOOT", $i); | 2803 | $reboot = set_test_option("REBOOT", $i); |
| 2131 | $noclean = set_test_option("BUILD_NOCLEAN", $i); | 2804 | $noclean = set_test_option("BUILD_NOCLEAN", $i); |
| 2132 | $minconfig = set_test_option("MIN_CONFIG", $i); | 2805 | $minconfig = set_test_option("MIN_CONFIG", $i); |
| 2806 | $output_minconfig = set_test_option("OUTPUT_MIN_CONFIG", $i); | ||
| 2807 | $start_minconfig = set_test_option("START_MIN_CONFIG", $i); | ||
| 2808 | $ignore_config = set_test_option("IGNORE_CONFIG", $i); | ||
| 2133 | $run_test = set_test_option("TEST", $i); | 2809 | $run_test = set_test_option("TEST", $i); |
| 2134 | $addconfig = set_test_option("ADD_CONFIG", $i); | 2810 | $addconfig = set_test_option("ADD_CONFIG", $i); |
| 2135 | $reboot_type = set_test_option("REBOOT_TYPE", $i); | 2811 | $reboot_type = set_test_option("REBOOT_TYPE", $i); |
| @@ -2145,12 +2821,16 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
| 2145 | $sleep_time = set_test_option("SLEEP_TIME", $i); | 2821 | $sleep_time = set_test_option("SLEEP_TIME", $i); |
| 2146 | $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); | 2822 | $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); |
| 2147 | $patchcheck_sleep_time = set_test_option("PATCHCHECK_SLEEP_TIME", $i); | 2823 | $patchcheck_sleep_time = set_test_option("PATCHCHECK_SLEEP_TIME", $i); |
| 2824 | $ignore_warnings = set_test_option("IGNORE_WARNINGS", $i); | ||
| 2148 | $bisect_manual = set_test_option("BISECT_MANUAL", $i); | 2825 | $bisect_manual = set_test_option("BISECT_MANUAL", $i); |
| 2149 | $bisect_skip = set_test_option("BISECT_SKIP", $i); | 2826 | $bisect_skip = set_test_option("BISECT_SKIP", $i); |
| 2827 | $config_bisect_good = set_test_option("CONFIG_BISECT_GOOD", $i); | ||
| 2150 | $store_failures = set_test_option("STORE_FAILURES", $i); | 2828 | $store_failures = set_test_option("STORE_FAILURES", $i); |
| 2829 | $test_name = set_test_option("TEST_NAME", $i); | ||
| 2151 | $timeout = set_test_option("TIMEOUT", $i); | 2830 | $timeout = set_test_option("TIMEOUT", $i); |
| 2152 | $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); | 2831 | $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); |
| 2153 | $console = set_test_option("CONSOLE", $i); | 2832 | $console = set_test_option("CONSOLE", $i); |
| 2833 | $detect_triplefault = set_test_option("DETECT_TRIPLE_FAULT", $i); | ||
| 2154 | $success_line = set_test_option("SUCCESS_LINE", $i); | 2834 | $success_line = set_test_option("SUCCESS_LINE", $i); |
| 2155 | $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i); | 2835 | $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i); |
| 2156 | $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i); | 2836 | $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i); |
| @@ -2161,6 +2841,13 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
| 2161 | $target_image = set_test_option("TARGET_IMAGE", $i); | 2841 | $target_image = set_test_option("TARGET_IMAGE", $i); |
| 2162 | $localversion = set_test_option("LOCALVERSION", $i); | 2842 | $localversion = set_test_option("LOCALVERSION", $i); |
| 2163 | 2843 | ||
| 2844 | $start_minconfig_defined = 1; | ||
| 2845 | |||
| 2846 | if (!defined($start_minconfig)) { | ||
| 2847 | $start_minconfig_defined = 0; | ||
| 2848 | $start_minconfig = $minconfig; | ||
| 2849 | } | ||
| 2850 | |||
| 2164 | chdir $builddir || die "can't change directory to $builddir"; | 2851 | chdir $builddir || die "can't change directory to $builddir"; |
| 2165 | 2852 | ||
| 2166 | if (!-d $tmpdir) { | 2853 | if (!-d $tmpdir) { |
| @@ -2193,6 +2880,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
| 2193 | $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"}; | 2880 | $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"}; |
| 2194 | } | 2881 | } |
| 2195 | 2882 | ||
| 2883 | if ($test_type eq "make_min_config") { | ||
| 2884 | $run_type = ""; | ||
| 2885 | } | ||
| 2886 | |||
| 2196 | # mistake in config file? | 2887 | # mistake in config file? |
| 2197 | if (!defined($run_type)) { | 2888 | if (!defined($run_type)) { |
| 2198 | $run_type = "ERROR"; | 2889 | $run_type = "ERROR"; |
| @@ -2204,11 +2895,12 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
| 2204 | unlink $dmesg; | 2895 | unlink $dmesg; |
| 2205 | unlink $buildlog; | 2896 | unlink $buildlog; |
| 2206 | 2897 | ||
| 2207 | if (!defined($minconfig)) { | 2898 | if (defined($addconfig)) { |
| 2208 | $minconfig = $addconfig; | 2899 | my $min = $minconfig; |
| 2209 | 2900 | if (!defined($minconfig)) { | |
| 2210 | } elsif (defined($addconfig)) { | 2901 | $min = ""; |
| 2211 | run_command "cat $addconfig $minconfig > $tmpdir/add_config" or | 2902 | } |
| 2903 | run_command "cat $addconfig $min > $tmpdir/add_config" or | ||
| 2212 | dodie "Failed to create temp config"; | 2904 | dodie "Failed to create temp config"; |
| 2213 | $minconfig = "$tmpdir/add_config"; | 2905 | $minconfig = "$tmpdir/add_config"; |
| 2214 | } | 2906 | } |
| @@ -2228,6 +2920,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
| 2228 | } elsif ($test_type eq "patchcheck") { | 2920 | } elsif ($test_type eq "patchcheck") { |
| 2229 | patchcheck $i; | 2921 | patchcheck $i; |
| 2230 | next; | 2922 | next; |
| 2923 | } elsif ($test_type eq "make_min_config") { | ||
| 2924 | make_min_config $i; | ||
| 2925 | next; | ||
| 2231 | } | 2926 | } |
| 2232 | 2927 | ||
| 2233 | if ($build_type ne "nobuild") { | 2928 | if ($build_type ne "nobuild") { |
| @@ -2235,13 +2930,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
| 2235 | } | 2930 | } |
| 2236 | 2931 | ||
| 2237 | if ($test_type ne "build") { | 2932 | if ($test_type ne "build") { |
| 2238 | get_grub_index; | ||
| 2239 | get_version; | ||
| 2240 | install; | ||
| 2241 | |||
| 2242 | my $failed = 0; | 2933 | my $failed = 0; |
| 2243 | start_monitor; | 2934 | start_monitor_and_boot or $failed = 1; |
| 2244 | monitor or $failed = 1;; | ||
| 2245 | 2935 | ||
| 2246 | if (!$failed && $test_type ne "boot" && defined($run_test)) { | 2936 | if (!$failed && $test_type ne "boot" && defined($run_test)) { |
| 2247 | do_run_test or $failed = 1; | 2937 | do_run_test or $failed = 1; |
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 48cbcc80602a..b8bcd14b5a4d 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf | |||
| @@ -293,6 +293,38 @@ | |||
| 293 | # or on some systems: | 293 | # or on some systems: |
| 294 | #POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION | 294 | #POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION |
| 295 | 295 | ||
| 296 | # If there is a script that you require to run before the build is done | ||
| 297 | # you can specify it with PRE_BUILD. | ||
| 298 | # | ||
| 299 | # One example may be if you must add a temporary patch to the build to | ||
| 300 | # fix a unrelated bug to perform a patchcheck test. This will apply the | ||
| 301 | # patch before each build that is made. Use the POST_BUILD to do a git reset --hard | ||
| 302 | # to remove the patch. | ||
| 303 | # | ||
| 304 | # (default undef) | ||
| 305 | #PRE_BUILD = cd ${BUILD_DIR} && patch -p1 < /tmp/temp.patch | ||
| 306 | |||
| 307 | # To specify if the test should fail if the PRE_BUILD fails, | ||
| 308 | # PRE_BUILD_DIE needs to be set to 1. Otherwise the PRE_BUILD | ||
| 309 | # result is ignored. | ||
| 310 | # (default 0) | ||
| 311 | # PRE_BUILD_DIE = 1 | ||
| 312 | |||
| 313 | # If there is a script that should run after the build is done | ||
| 314 | # you can specify it with POST_BUILD. | ||
| 315 | # | ||
| 316 | # As the example in PRE_BUILD, POST_BUILD can be used to reset modifications | ||
| 317 | # made by the PRE_BUILD. | ||
| 318 | # | ||
| 319 | # (default undef) | ||
| 320 | #POST_BUILD = cd ${BUILD_DIR} && git reset --hard | ||
| 321 | |||
| 322 | # To specify if the test should fail if the POST_BUILD fails, | ||
| 323 | # POST_BUILD_DIE needs to be set to 1. Otherwise the POST_BUILD | ||
| 324 | # result is ignored. | ||
| 325 | # (default 0) | ||
| 326 | #POST_BUILD_DIE = 1 | ||
| 327 | |||
| 296 | # Way to reboot the box to the test kernel. | 328 | # Way to reboot the box to the test kernel. |
| 297 | # Only valid options so far are "grub" and "script" | 329 | # Only valid options so far are "grub" and "script" |
| 298 | # (default grub) | 330 | # (default grub) |
| @@ -360,8 +392,8 @@ | |||
| 360 | #ADD_CONFIG = /home/test/config-broken | 392 | #ADD_CONFIG = /home/test/config-broken |
| 361 | 393 | ||
| 362 | # The location on the host where to write temp files | 394 | # The location on the host where to write temp files |
| 363 | # (default /tmp/ktest) | 395 | # (default /tmp/ktest/${MACHINE}) |
| 364 | #TMP_DIR = /tmp/ktest | 396 | #TMP_DIR = /tmp/ktest/${MACHINE} |
| 365 | 397 | ||
| 366 | # Optional log file to write the status (recommended) | 398 | # Optional log file to write the status (recommended) |
| 367 | # Note, this is a DEFAULT section only option. | 399 | # Note, this is a DEFAULT section only option. |
| @@ -518,6 +550,16 @@ | |||
| 518 | # The variables SSH_USER and MACHINE are defined. | 550 | # The variables SSH_USER and MACHINE are defined. |
| 519 | #REBOOT = ssh $SSH_USER@$MACHINE reboot | 551 | #REBOOT = ssh $SSH_USER@$MACHINE reboot |
| 520 | 552 | ||
| 553 | # The way triple faults are detected is by testing the kernel | ||
| 554 | # banner. If the kernel banner for the kernel we are testing is | ||
| 555 | # found, and then later a kernel banner for another kernel version | ||
| 556 | # is found, it is considered that we encountered a triple fault, | ||
| 557 | # and there is no panic or callback, but simply a reboot. | ||
| 558 | # To disable this (because it did a false positive) set the following | ||
| 559 | # to 0. | ||
| 560 | # (default 1) | ||
| 561 | #DETECT_TRIPLE_FAULT = 0 | ||
| 562 | |||
| 521 | #### Per test run options #### | 563 | #### Per test run options #### |
| 522 | # The following options are only allowed in TEST_START sections. | 564 | # The following options are only allowed in TEST_START sections. |
| 523 | # They are ignored in the DEFAULTS sections. | 565 | # They are ignored in the DEFAULTS sections. |
| @@ -535,6 +577,12 @@ | |||
| 535 | # all preceding tests until a new CHECKOUT is set. | 577 | # all preceding tests until a new CHECKOUT is set. |
| 536 | # | 578 | # |
| 537 | # | 579 | # |
| 580 | # TEST_NAME = name | ||
| 581 | # | ||
| 582 | # If you want the test to have a name that is displayed in | ||
| 583 | # the test result banner at the end of the test, then use this | ||
| 584 | # option. This is useful to search for the RESULT keyword and | ||
| 585 | # not have to translate a test number to a test in the config. | ||
| 538 | # | 586 | # |
| 539 | # For TEST_TYPE = patchcheck | 587 | # For TEST_TYPE = patchcheck |
| 540 | # | 588 | # |
| @@ -556,7 +604,12 @@ | |||
| 556 | # build, boot, test. | 604 | # build, boot, test. |
| 557 | # | 605 | # |
| 558 | # Note, the build test will look for warnings, if a warning occurred | 606 | # Note, the build test will look for warnings, if a warning occurred |
| 559 | # in a file that a commit touches, the build will fail. | 607 | # in a file that a commit touches, the build will fail, unless |
| 608 | # IGNORE_WARNINGS is set for the given commit's sha1 | ||
| 609 | # | ||
| 610 | # IGNORE_WARNINGS can be used to disable the failure of patchcheck | ||
| 611 | # on a particuler commit (SHA1). You can add more than one commit | ||
| 612 | # by adding a list of SHA1s that are space delimited. | ||
| 560 | # | 613 | # |
| 561 | # If BUILD_NOCLEAN is set, then make mrproper will not be run on | 614 | # If BUILD_NOCLEAN is set, then make mrproper will not be run on |
| 562 | # any of the builds, just like all other TEST_TYPE tests. But | 615 | # any of the builds, just like all other TEST_TYPE tests. But |
| @@ -571,6 +624,7 @@ | |||
| 571 | # PATCHCHECK_TYPE = boot | 624 | # PATCHCHECK_TYPE = boot |
| 572 | # PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 | 625 | # PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 |
| 573 | # PATCHCHECK_END = HEAD~2 | 626 | # PATCHCHECK_END = HEAD~2 |
| 627 | # IGNORE_WARNINGS = 42f9c6b69b54946ffc0515f57d01dc7f5c0e4712 0c17ca2c7187f431d8ffc79e81addc730f33d128 | ||
| 574 | # | 628 | # |
| 575 | # | 629 | # |
| 576 | # | 630 | # |
| @@ -739,13 +793,18 @@ | |||
| 739 | # boot - bad builds but fails to boot | 793 | # boot - bad builds but fails to boot |
| 740 | # test - bad boots but fails a test | 794 | # test - bad boots but fails a test |
| 741 | # | 795 | # |
| 742 | # CONFIG_BISECT is the config that failed to boot | 796 | # CONFIG_BISECT is the config that failed to boot |
| 743 | # | 797 | # |
| 744 | # If BISECT_MANUAL is set, it will pause between iterations. | 798 | # If BISECT_MANUAL is set, it will pause between iterations. |
| 745 | # This is useful to use just ktest.pl just for the config bisect. | 799 | # This is useful to use just ktest.pl just for the config bisect. |
| 746 | # If you set it to build, it will run the bisect and you can | 800 | # If you set it to build, it will run the bisect and you can |
| 747 | # control what happens in between iterations. It will ask you if | 801 | # control what happens in between iterations. It will ask you if |
| 748 | # the test succeeded or not and continue the config bisect. | 802 | # the test succeeded or not and continue the config bisect. |
| 803 | # | ||
| 804 | # CONFIG_BISECT_GOOD (optional) | ||
| 805 | # If you have a good config to start with, then you | ||
| 806 | # can specify it with CONFIG_BISECT_GOOD. Otherwise | ||
| 807 | # the MIN_CONFIG is the base. | ||
| 749 | # | 808 | # |
| 750 | # Example: | 809 | # Example: |
| 751 | # TEST_START | 810 | # TEST_START |
| @@ -755,3 +814,68 @@ | |||
| 755 | # MIN_CONFIG = /home/test/config-min | 814 | # MIN_CONFIG = /home/test/config-min |
| 756 | # BISECT_MANUAL = 1 | 815 | # BISECT_MANUAL = 1 |
| 757 | # | 816 | # |
| 817 | # | ||
| 818 | # | ||
| 819 | # For TEST_TYPE = make_min_config | ||
| 820 | # | ||
| 821 | # After doing a make localyesconfig, your kernel configuration may | ||
| 822 | # not be the most useful minimum configuration. Having a true minimum | ||
| 823 | # config that you can use against other configs is very useful if | ||
| 824 | # someone else has a config that breaks on your code. By only forcing | ||
| 825 | # those configurations that are truly required to boot your machine | ||
| 826 | # will give you less of a chance that one of your set configurations | ||
| 827 | # will make the bug go away. This will give you a better chance to | ||
| 828 | # be able to reproduce the reported bug matching the broken config. | ||
| 829 | # | ||
| 830 | # Note, this does take some time, and may require you to run the | ||
| 831 | # test over night, or perhaps over the weekend. But it also allows | ||
| 832 | # you to interrupt it, and gives you the current minimum config | ||
| 833 | # that was found till that time. | ||
| 834 | # | ||
| 835 | # Note, this test automatically assumes a BUILD_TYPE of oldconfig | ||
| 836 | # and its test type acts like boot. | ||
| 837 | # TODO: add a test version that makes the config do more than just | ||
| 838 | # boot, like having network access. | ||
| 839 | # | ||
| 840 | # To save time, the test does not just grab any option and test | ||
| 841 | # it. The Kconfig files are examined to determine the dependencies | ||
| 842 | # of the configs. If a config is chosen that depends on another | ||
| 843 | # config, that config will be checked first. By checking the | ||
| 844 | # parents first, we can eliminate whole groups of configs that | ||
| 845 | # may have been enabled. | ||
| 846 | # | ||
| 847 | # For example, if a USB device config is chosen and depends on CONFIG_USB, | ||
| 848 | # the CONFIG_USB will be tested before the device. If CONFIG_USB is | ||
| 849 | # found not to be needed, it, as well as all configs that depend on | ||
| 850 | # it, will be disabled and removed from the current min_config. | ||
| 851 | # | ||
| 852 | # OUTPUT_MIN_CONFIG is the path and filename of the file that will | ||
| 853 | # be created from the MIN_CONFIG. If you interrupt the test, set | ||
| 854 | # this file as your new min config, and use it to continue the test. | ||
| 855 | # This file does not need to exist on start of test. | ||
| 856 | # This file is not created until a config is found that can be removed. | ||
| 857 | # If this file exists, you will be prompted if you want to use it | ||
| 858 | # as the min_config (overriding MIN_CONFIG) if START_MIN_CONFIG | ||
| 859 | # is not defined. | ||
| 860 | # (required field) | ||
| 861 | # | ||
| 862 | # START_MIN_CONFIG is the config to use to start the test with. | ||
| 863 | # you can set this as the same OUTPUT_MIN_CONFIG, but if you do | ||
| 864 | # the OUTPUT_MIN_CONFIG file must exist. | ||
| 865 | # (default MIN_CONFIG) | ||
| 866 | # | ||
| 867 | # IGNORE_CONFIG is used to specify a config file that has configs that | ||
| 868 | # you already know must be set. Configs are written here that have | ||
| 869 | # been tested and proved to be required. It is best to define this | ||
| 870 | # file if you intend on interrupting the test and running it where | ||
| 871 | # it left off. New configs that it finds will be written to this file | ||
| 872 | # and will not be tested again in later runs. | ||
| 873 | # (optional) | ||
| 874 | # | ||
| 875 | # Example: | ||
| 876 | # | ||
| 877 | # TEST_TYPE = make_min_config | ||
| 878 | # OUTPUT_MIN_CONFIG = /path/to/config-new-min | ||
| 879 | # START_MIN_CONFIG = /path/to/config-min | ||
| 880 | # IGNORE_CONFIG = /path/to/config-tested | ||
| 881 | # | ||
