diff options
Diffstat (limited to 'tools')
28 files changed, 757 insertions, 225 deletions
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt new file mode 100644 index 000000000000..0cada9e053dc --- /dev/null +++ b/tools/perf/Documentation/perf-evlist.txt @@ -0,0 +1,26 @@ +perf-evlist(1) +============== + +NAME +---- +perf-evlist - List the event names in a perf.data file + +SYNOPSIS +-------- +[verse] +'perf evlist <options>' + +DESCRIPTION +----------- +This command displays the names of events sampled in a perf.data file. + +OPTIONS +------- +-i:: +--input=:: + Input file name. (default: perf.data) + +SEE ALSO +-------- +linkperf:perf-record[1], linkperf:perf-list[1], +linkperf:perf-report[1] diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 29ad94293cd2..66f040b30729 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -112,6 +112,28 @@ OPTIONS --debug-mode:: Do various checks like samples ordering and lost events. +-f:: +--fields + Comma separated list of fields to print. Options are: + comm, tid, pid, time, cpu, event, trace, sym. Field + list must be prepended with the type, trace, sw or hw, + to indicate to which event type the field list applies. + e.g., -f sw:comm,tid,time,sym and -f trace:time,cpu,trace + +-k:: +--vmlinux=<file>:: + vmlinux pathname + +--kallsyms=<file>:: + kallsyms pathname + +--symfs=<directory>:: + Look for files with symbols relative to this directory. + +-G:: +--hide-call-graph:: + When printing symbols do not display call chain. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script-perl[1], diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 9b8421805c5c..158c30e8210c 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -338,6 +338,7 @@ endif BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o BUILTIN_OBJS += $(OUTPUT)builtin-diff.o +BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o BUILTIN_OBJS += $(OUTPUT)builtin-help.o BUILTIN_OBJS += $(OUTPUT)builtin-sched.o BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c new file mode 100644 index 000000000000..4c5e9e04a41f --- /dev/null +++ b/tools/perf/builtin-evlist.c @@ -0,0 +1,54 @@ +/* + * Builtin evlist command: Show the list of event selectors present + * in a perf.data file. + */ +#include "builtin.h" + +#include "util/util.h" + +#include <linux/list.h> + +#include "perf.h" +#include "util/evlist.h" +#include "util/evsel.h" +#include "util/parse-events.h" +#include "util/parse-options.h" +#include "util/session.h" + +static char const *input_name = "perf.data"; + +static int __cmd_evlist(void) +{ + struct perf_session *session; + struct perf_evsel *pos; + + session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); + if (session == NULL) + return -ENOMEM; + + list_for_each_entry(pos, &session->evlist->entries, node) + printf("%s\n", event_name(pos)); + + perf_session__delete(session); + return 0; +} + +static const char * const evlist_usage[] = { + "perf evlist [<options>]", + NULL +}; + +static const struct option options[] = { + OPT_STRING('i', "input", &input_name, "file", + "input file name"), + OPT_END() +}; + +int cmd_evlist(int argc, const char **argv, const char *prefix __used) +{ + argc = parse_options(argc, argv, options, evlist_usage, 0); + if (argc) + usage_with_options(evlist_usage, options); + + return __cmd_evlist(); +} diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 2e93f99b1480..7a2a79d2cf2c 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -202,9 +202,20 @@ static struct thread_stat *thread_stat_findnew_first(u32 tid) SINGLE_KEY(nr_acquired) SINGLE_KEY(nr_contended) SINGLE_KEY(wait_time_total) -SINGLE_KEY(wait_time_min) SINGLE_KEY(wait_time_max) +static int lock_stat_key_wait_time_min(struct lock_stat *one, + struct lock_stat *two) +{ + u64 s1 = one->wait_time_min; + u64 s2 = two->wait_time_min; + if (s1 == ULLONG_MAX) + s1 = 0; + if (s2 == ULLONG_MAX) + s2 = 0; + return s1 > s2; +} + struct lock_key { /* * name: the value for specify by user diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 5f40df635dcb..9f5fc5492141 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -12,6 +12,8 @@ #include "util/trace-event.h" #include "util/parse-options.h" #include "util/util.h" +#include "util/evlist.h" +#include "util/evsel.h" static char const *script_name; static char const *generate_script_lang; @@ -19,6 +21,183 @@ static bool debug_mode; static u64 last_timestamp; static u64 nr_unordered; extern const struct option record_options[]; +static bool no_callchain; + +enum perf_output_field { + PERF_OUTPUT_COMM = 1U << 0, + PERF_OUTPUT_TID = 1U << 1, + PERF_OUTPUT_PID = 1U << 2, + PERF_OUTPUT_TIME = 1U << 3, + PERF_OUTPUT_CPU = 1U << 4, + PERF_OUTPUT_EVNAME = 1U << 5, + PERF_OUTPUT_TRACE = 1U << 6, + PERF_OUTPUT_SYM = 1U << 7, +}; + +struct output_option { + const char *str; + enum perf_output_field field; +} all_output_options[] = { + {.str = "comm", .field = PERF_OUTPUT_COMM}, + {.str = "tid", .field = PERF_OUTPUT_TID}, + {.str = "pid", .field = PERF_OUTPUT_PID}, + {.str = "time", .field = PERF_OUTPUT_TIME}, + {.str = "cpu", .field = PERF_OUTPUT_CPU}, + {.str = "event", .field = PERF_OUTPUT_EVNAME}, + {.str = "trace", .field = PERF_OUTPUT_TRACE}, + {.str = "sym", .field = PERF_OUTPUT_SYM}, +}; + +/* default set to maintain compatibility with current format */ +static u64 output_fields[PERF_TYPE_MAX] = { + [PERF_TYPE_HARDWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ + PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ + PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, + + [PERF_TYPE_SOFTWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ + PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ + PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, + + [PERF_TYPE_TRACEPOINT] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ + PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ + PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, +}; + +static bool output_set_by_user; + +#define PRINT_FIELD(x) (output_fields[attr->type] & PERF_OUTPUT_##x) + +static int perf_session__check_attr(struct perf_session *session, + struct perf_event_attr *attr) +{ + if (PRINT_FIELD(TRACE) && + !perf_session__has_traces(session, "record -R")) + return -EINVAL; + + if (PRINT_FIELD(SYM)) { + if (!(session->sample_type & PERF_SAMPLE_IP)) { + pr_err("Samples do not contain IP data.\n"); + return -EINVAL; + } + if (!no_callchain && + !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) + symbol_conf.use_callchain = false; + } + + if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && + !(session->sample_type & PERF_SAMPLE_TID)) { + pr_err("Samples do not contain TID/PID data.\n"); + return -EINVAL; + } + + if (PRINT_FIELD(TIME) && + !(session->sample_type & PERF_SAMPLE_TIME)) { + pr_err("Samples do not contain timestamps.\n"); + return -EINVAL; + } + + if (PRINT_FIELD(CPU) && + !(session->sample_type & PERF_SAMPLE_CPU)) { + pr_err("Samples do not contain cpu.\n"); + return -EINVAL; + } + + return 0; +} + +static void print_sample_start(struct perf_sample *sample, + struct thread *thread, + struct perf_event_attr *attr) +{ + int type; + struct event *event; + const char *evname = NULL; + unsigned long secs; + unsigned long usecs; + unsigned long long nsecs; + + if (PRINT_FIELD(COMM)) { + if (latency_format) + printf("%8.8s ", thread->comm); + else if (PRINT_FIELD(SYM) && symbol_conf.use_callchain) + printf("%s ", thread->comm); + else + printf("%16s ", thread->comm); + } + + if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) + printf("%5d/%-5d ", sample->pid, sample->tid); + else if (PRINT_FIELD(PID)) + printf("%5d ", sample->pid); + else if (PRINT_FIELD(TID)) + printf("%5d ", sample->tid); + + if (PRINT_FIELD(CPU)) { + if (latency_format) + printf("%3d ", sample->cpu); + else + printf("[%03d] ", sample->cpu); + } + + if (PRINT_FIELD(TIME)) { + nsecs = sample->time; + secs = nsecs / NSECS_PER_SEC; + nsecs -= secs * NSECS_PER_SEC; + usecs = nsecs / NSECS_PER_USEC; + printf("%5lu.%06lu: ", secs, usecs); + } + + if (PRINT_FIELD(EVNAME)) { + if (attr->type == PERF_TYPE_TRACEPOINT) { + type = trace_parse_common_type(sample->raw_data); + event = trace_find_event(type); + if (event) + evname = event->name; + } else + evname = __event_name(attr->type, attr->config); + + printf("%s: ", evname ? evname : "(unknown)"); + } +} + +static void process_event(union perf_event *event __unused, + struct perf_sample *sample, + struct perf_session *session, + struct thread *thread) +{ + struct perf_event_attr *attr; + struct perf_evsel *evsel; + + evsel = perf_evlist__id2evsel(session->evlist, sample->id); + if (evsel == NULL) { + pr_err("Invalid data. Contains samples with id not in " + "its header!\n"); + return; + } + attr = &evsel->attr; + + if (output_fields[attr->type] == 0) + return; + + if (perf_session__check_attr(session, attr) < 0) + return; + + print_sample_start(sample, thread, attr); + + if (PRINT_FIELD(TRACE)) + print_trace_event(sample->cpu, sample->raw_data, + sample->raw_size); + + if (PRINT_FIELD(SYM)) { + if (!symbol_conf.use_callchain) + printf(" "); + else + printf("\n"); + perf_session__print_symbols(event, sample, session); + } + + printf("\n"); +} static int default_start_script(const char *script __unused, int argc __unused, @@ -40,7 +219,7 @@ static int default_generate_script(const char *outfile __unused) static struct scripting_ops default_scripting_ops = { .start_script = default_start_script, .stop_script = default_stop_script, - .process_event = print_event, + .process_event = process_event, .generate_script = default_generate_script, }; @@ -75,26 +254,17 @@ static int process_sample_event(union perf_event *event, return -1; } - if (session->sample_type & PERF_SAMPLE_RAW) { - if (debug_mode) { - if (sample->time < last_timestamp) { - pr_err("Samples misordered, previous: %" PRIu64 - " this: %" PRIu64 "\n", last_timestamp, - sample->time); - nr_unordered++; - } - last_timestamp = sample->time; - return 0; + if (debug_mode) { + if (sample->time < last_timestamp) { + pr_err("Samples misordered, previous: %" PRIu64 + " this: %" PRIu64 "\n", last_timestamp, + sample->time); + nr_unordered++; } - /* - * FIXME: better resolve from pid from the struct trace_entry - * field, although it should be the same than this perf - * event pid - */ - scripting_ops->process_event(sample->cpu, sample->raw_data, - sample->raw_size, - sample->time, thread->comm); + last_timestamp = sample->time; + return 0; } + scripting_ops->process_event(event, sample, session, thread); session->hists.stats.total_period += sample->period; return 0; @@ -102,7 +272,10 @@ static int process_sample_event(union perf_event *event, static struct perf_event_ops event_ops = { .sample = process_sample_event, + .mmap = perf_event__process_mmap, .comm = perf_event__process_comm, + .exit = perf_event__process_task, + .fork = perf_event__process_task, .attr = perf_event__process_attr, .event_type = perf_event__process_event_type, .tracing_data = perf_event__process_tracing_data, @@ -280,6 +453,68 @@ static int parse_scriptname(const struct option *opt __used, return 0; } +static int parse_output_fields(const struct option *opt __used, + const char *arg, int unset __used) +{ + char *tok; + int i, imax = sizeof(all_output_options) / sizeof(struct output_option); + int rc = 0; + char *str = strdup(arg); + int type = -1; + + if (!str) + return -ENOMEM; + + tok = strtok(str, ":"); + if (!tok) { + fprintf(stderr, + "Invalid field string - not prepended with type."); + return -EINVAL; + } + + /* first word should state which event type user + * is specifying the fields + */ + if (!strcmp(tok, "hw")) + type = PERF_TYPE_HARDWARE; + else if (!strcmp(tok, "sw")) + type = PERF_TYPE_SOFTWARE; + else if (!strcmp(tok, "trace")) + type = PERF_TYPE_TRACEPOINT; + else { + fprintf(stderr, "Invalid event type in field string."); + return -EINVAL; + } + + output_fields[type] = 0; + while (1) { + tok = strtok(NULL, ","); + if (!tok) + break; + for (i = 0; i < imax; ++i) { + if (strcmp(tok, all_output_options[i].str) == 0) { + output_fields[type] |= all_output_options[i].field; + break; + } + } + if (i == imax) { + fprintf(stderr, "Invalid field requested."); + rc = -EINVAL; + break; + } + } + + if (output_fields[type] == 0) { + pr_debug("No fields requested for %s type. " + "Events will not be displayed\n", event_type(type)); + } + + output_set_by_user = true; + + free(str); + return rc; +} + /* Helper function for filesystems that return a dent->d_type DT_UNKNOWN */ static int is_directory(const char *base_path, const struct dirent *dent) { @@ -592,6 +827,17 @@ static const struct option options[] = { "input file name"), OPT_BOOLEAN('d', "debug-mode", &debug_mode, "do various checks like samples ordering and lost events"), + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), + OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, + "file", "kallsyms pathname"), + OPT_BOOLEAN('G', "hide-call-graph", &no_callchain, + "When printing symbols do not display call chain"), + OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", + "Look for files with symbols relative to this directory"), + OPT_CALLBACK('f', "fields", NULL, "str", + "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace. Fields: comm,tid,pid,time,cpu,event,trace,sym", + parse_output_fields), OPT_END() }; @@ -772,14 +1018,22 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) if (session == NULL) return -ENOMEM; - if (strcmp(input_name, "-") && - !perf_session__has_traces(session, "record -R")) - return -EINVAL; + if (!no_callchain) + symbol_conf.use_callchain = true; + else + symbol_conf.use_callchain = false; if (generate_script_lang) { struct stat perf_stat; + int input; + + if (output_set_by_user) { + fprintf(stderr, + "custom fields not supported for generated scripts"); + return -1; + } - int input = open(input_name, O_RDONLY); + input = open(input_name, O_RDONLY); if (input < 0) { perror("failed to open file"); exit(-1); diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 21c025222496..e2109f9b43eb 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -333,6 +333,12 @@ static int run_perf_stat(int argc __used, const char **argv) } } + if (perf_evlist__set_filters(evsel_list)) { + error("failed to set filter with %d (%s)\n", errno, + strerror(errno)); + return -1; + } + /* * Enable counters and exec the command: */ @@ -634,6 +640,8 @@ static const struct option options[] = { OPT_CALLBACK('e', "event", &evsel_list, "event", "event selector. use 'perf list' to list available events", parse_events), + OPT_CALLBACK(0, "filter", &evsel_list, "filter", + "event filter", parse_filter), OPT_BOOLEAN('i', "no-inherit", &no_inherit, "child tasks do not inherit counters"), OPT_INTEGER('p', "pid", &target_pid, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 80c9e062bd5b..70f1075cc5b0 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -152,7 +152,7 @@ static int parse_source(struct sym_entry *syme) /* * We can't annotate with just /proc/kallsyms */ - if (map->dso->origin == DSO__ORIG_KERNEL) { + if (map->dso->symtab_type == SYMTAB__KALLSYMS) { pr_err("Can't annotate %s: No vmlinux file was found in the " "path\n", sym->name); sleep(1); @@ -515,24 +515,25 @@ static void handle_keypress(struct perf_session *session, int c) break; case 'E': if (top.evlist->nr_entries > 1) { + int counter; fprintf(stderr, "\nAvailable events:"); list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) fprintf(stderr, "\n\t%d %s", top.sym_evsel->idx, event_name(top.sym_evsel)); - prompt_integer(&top.sym_counter, "Enter details event counter"); + prompt_integer(&counter, "Enter details event counter"); - if (top.sym_counter >= top.evlist->nr_entries) { + if (counter >= top.evlist->nr_entries) { top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); - top.sym_counter = 0; fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top.sym_evsel)); sleep(1); break; } list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) - if (top.sym_evsel->idx == top.sym_counter) + if (top.sym_evsel->idx == counter) break; - } else top.sym_counter = 0; + } else + top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); break; case 'f': prompt_integer(&top.count_filter, "Enter display event count filter"); @@ -675,7 +676,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) for (i = 0; skip_symbols[i]; i++) { if (!strcmp(skip_symbols[i], name)) { - syme->skip = 1; + sym->ignore = true; break; } } @@ -768,7 +769,7 @@ static void perf_event__process_sample(const union perf_event *event, struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); pr_err("Can't annotate %s", sym->name); - if (top.sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) { + if (top.sym_filter_entry->map->dso->symtab_type == SYMTAB__KALLSYMS) { pr_err(": No vmlinux file was found in the path:\n"); machine__fprintf_vmlinux_path(machine, stderr); } else @@ -778,10 +779,9 @@ static void perf_event__process_sample(const union perf_event *event, } syme = symbol__priv(al.sym); - if (!syme->skip) { + if (!al.sym->ignore) { struct perf_evsel *evsel; - syme->origin = origin; evsel = perf_evlist__id2evsel(top.evlist, sample->id); assert(evsel != NULL); syme->count[evsel->idx]++; diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index c7798c7f24ed..4702e2443a8e 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -19,6 +19,7 @@ extern int cmd_bench(int argc, const char **argv, const char *prefix); extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); extern int cmd_diff(int argc, const char **argv, const char *prefix); +extern int cmd_evlist(int argc, const char **argv, const char *prefix); extern int cmd_help(int argc, const char **argv, const char *prefix); extern int cmd_sched(int argc, const char **argv, const char *prefix); extern int cmd_list(int argc, const char **argv, const char *prefix); diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 16b5088cf8f4..d695fe40fbff 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -8,6 +8,7 @@ perf-bench mainporcelain common perf-buildid-cache mainporcelain common perf-buildid-list mainporcelain common perf-diff mainporcelain common +perf-evlist mainporcelain common perf-inject mainporcelain common perf-list mainporcelain common perf-sched mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 595d0f4a7103..ec635b7cc8ea 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -313,6 +313,7 @@ static void handle_internal_command(int argc, const char **argv) { "buildid-cache", cmd_buildid_cache, 0 }, { "buildid-list", cmd_buildid_list, 0 }, { "diff", cmd_diff, 0 }, + { "evlist", cmd_evlist, 0 }, { "help", cmd_help, 0 }, { "list", cmd_list, 0 }, { "record", cmd_record, 0 }, diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 97d76562a1a0..26d4d3fd6deb 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -23,10 +23,10 @@ if test -d ../../.git -o -f ../../.git && then VN=$(echo "$VN" | sed -e 's/-/./g'); else - eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '` - eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '` - eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '` - eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '` + eval $(grep '^VERSION[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^PATCHLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^SUBLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^EXTRAVERSION[[:space:]]*=' ../../Makefile|tr -d ' ') VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" fi diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 0d0830c98cd7..e01af2b1a469 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -294,7 +294,7 @@ fallback: free_filename = false; } - if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->symtab_type == SYMTAB__KALLSYMS) { char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; char *build_id_msg = NULL; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 54a7e2634d58..952b4ae3d954 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -263,6 +263,28 @@ static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) return name; } +const char *event_type(int type) +{ + switch (type) { + case PERF_TYPE_HARDWARE: + return "hardware"; + + case PERF_TYPE_SOFTWARE: + return "software"; + + case PERF_TYPE_TRACEPOINT: + return "tracepoint"; + + case PERF_TYPE_HW_CACHE: + return "hardware-cache"; + + default: + break; + } + + return "unknown"; +} + const char *event_name(struct perf_evsel *evsel) { u64 config = evsel->attr.config; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 212f88e07a9c..746d3fcbfc2a 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -20,6 +20,7 @@ struct tracepoint_path { extern struct tracepoint_path *tracepoint_id_to_path(u64 config); extern bool have_tracepoints(struct list_head *evlist); +const char *event_type(int type); const char *event_name(struct perf_evsel *event); extern const char *__event_name(int type, u64 config); diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 93680818e244..621427212e86 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -245,9 +245,10 @@ static inline struct event *find_cache_event(int type) return event; } -static void perl_process_event(int cpu, void *data, - int size __unused, - unsigned long long nsecs, char *comm) +static void perl_process_event(union perf_event *pevent __unused, + struct perf_sample *sample, + struct perf_session *session __unused, + struct thread *thread) { struct format_field *field; static char handler[256]; @@ -256,6 +257,10 @@ static void perl_process_event(int cpu, void *data, struct event *event; int type; int pid; + int cpu = sample->cpu; + void *data = sample->raw_data; + unsigned long long nsecs = sample->time; + char *comm = thread->comm; dSP; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 2040b8538527..1b85d6055159 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -204,9 +204,10 @@ static inline struct event *find_cache_event(int type) return event; } -static void python_process_event(int cpu, void *data, - int size __unused, - unsigned long long nsecs, char *comm) +static void python_process_event(union perf_event *pevent __unused, + struct perf_sample *sample, + struct perf_session *session __unused, + struct thread *thread) { PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; static char handler_name[256]; @@ -217,6 +218,10 @@ static void python_process_event(int cpu, void *data, unsigned n = 0; int type; int pid; + int cpu = sample->cpu; + void *data = sample->raw_data; + unsigned long long nsecs = sample->time; + char *comm = thread->comm; t = PyTuple_New(MAX_FIELDS); if (!t) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f26639fa0fb3..c68cf40764f9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1134,3 +1134,64 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) return ret; } + +void perf_session__print_symbols(union perf_event *event, + struct perf_sample *sample, + struct perf_session *session) +{ + struct addr_location al; + const char *symname, *dsoname; + struct callchain_cursor *cursor = &session->callchain_cursor; + struct callchain_cursor_node *node; + + if (perf_event__preprocess_sample(event, session, &al, sample, + NULL) < 0) { + error("problem processing %d event, skipping it.\n", + event->header.type); + return; + } + + if (symbol_conf.use_callchain && sample->callchain) { + + if (perf_session__resolve_callchain(session, al.thread, + sample->callchain, NULL) != 0) { + if (verbose) + error("Failed to resolve callchain. Skipping\n"); + return; + } + callchain_cursor_commit(cursor); + + while (1) { + node = callchain_cursor_current(cursor); + if (!node) + break; + + if (node->sym && node->sym->name) + symname = node->sym->name; + else + symname = ""; + + if (node->map && node->map->dso && node->map->dso->name) + dsoname = node->map->dso->name; + else + dsoname = ""; + + printf("\t%16" PRIx64 " %s (%s)\n", node->ip, symname, dsoname); + + callchain_cursor_advance(cursor); + } + + } else { + if (al.sym && al.sym->name) + symname = al.sym->name; + else + symname = ""; + + if (al.map && al.map->dso && al.map->dso->name) + dsoname = al.map->dso->name; + else + dsoname = ""; + + printf("%16" PRIx64 " %s (%s)", al.addr, symname, dsoname); + } +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index b5b148b0aaca..0b3c9afecaa9 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -159,4 +159,8 @@ static inline int perf_session__parse_sample(struct perf_session *session, session->sample_id_all, sample); } +void perf_session__print_symbols(union perf_event *event, + struct perf_sample *sample, + struct perf_session *session); + #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 00014e32c288..651dbfe7f4f3 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -207,7 +207,7 @@ struct dso *dso__new(const char *name) dso__set_short_name(self, self->name); for (i = 0; i < MAP__NR_TYPES; ++i) self->symbols[i] = self->symbol_names[i] = RB_ROOT; - self->origin = DSO__ORIG_NOT_FOUND; + self->symtab_type = SYMTAB__NOT_FOUND; self->loaded = 0; self->sorted_by_name = 0; self->has_build_id = 0; @@ -680,9 +680,9 @@ int dso__load_kallsyms(struct dso *self, const char *filename, return -1; if (self->kernel == DSO_TYPE_GUEST_KERNEL) - self->origin = DSO__ORIG_GUEST_KERNEL; + self->symtab_type = SYMTAB__GUEST_KALLSYMS; else - self->origin = DSO__ORIG_KERNEL; + self->symtab_type = SYMTAB__KALLSYMS; return dso__split_kallsyms(self, map, filter); } @@ -1204,7 +1204,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, } curr_map->map_ip = identity__map_ip; curr_map->unmap_ip = identity__map_ip; - curr_dso->origin = self->origin; + curr_dso->symtab_type = self->symtab_type; map_groups__insert(kmap->kmaps, curr_map); dsos__add(&self->node, curr_dso); dso__set_loaded(curr_dso, map->type); @@ -1430,21 +1430,21 @@ out: char dso__symtab_origin(const struct dso *self) { static const char origin[] = { - [DSO__ORIG_KERNEL] = 'k', - [DSO__ORIG_JAVA_JIT] = 'j', - [DSO__ORIG_BUILD_ID_CACHE] = 'B', - [DSO__ORIG_FEDORA] = 'f', - [DSO__ORIG_UBUNTU] = 'u', - [DSO__ORIG_BUILDID] = 'b', - [DSO__ORIG_DSO] = 'd', - [DSO__ORIG_KMODULE] = 'K', - [DSO__ORIG_GUEST_KERNEL] = 'g', - [DSO__ORIG_GUEST_KMODULE] = 'G', + [SYMTAB__KALLSYMS] = 'k', + [SYMTAB__JAVA_JIT] = 'j', + [SYMTAB__BUILD_ID_CACHE] = 'B', + [SYMTAB__FEDORA_DEBUGINFO] = 'f', + [SYMTAB__UBUNTU_DEBUGINFO] = 'u', + [SYMTAB__BUILDID_DEBUGINFO] = 'b', + [SYMTAB__SYSTEM_PATH_DSO] = 'd', + [SYMTAB__SYSTEM_PATH_KMODULE] = 'K', + [SYMTAB__GUEST_KALLSYMS] = 'g', + [SYMTAB__GUEST_KMODULE] = 'G', }; - if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) + if (self == NULL || self->symtab_type == SYMTAB__NOT_FOUND) return '!'; - return origin[self->origin]; + return origin[self->symtab_type]; } int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) @@ -1477,8 +1477,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) if (strncmp(self->name, "/tmp/perf-", 10) == 0) { ret = dso__load_perf_map(self, map, filter); - self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : - DSO__ORIG_NOT_FOUND; + self->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : + SYMTAB__NOT_FOUND; return ret; } @@ -1486,26 +1486,26 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) * On the first pass, only load images if they have a full symtab. * Failing that, do a second pass where we accept .dynsym also */ - for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1; - self->origin != DSO__ORIG_NOT_FOUND; - self->origin++) { - switch (self->origin) { - case DSO__ORIG_BUILD_ID_CACHE: + for (self->symtab_type = SYMTAB__BUILD_ID_CACHE, want_symtab = 1; + self->symtab_type != SYMTAB__NOT_FOUND; + self->symtab_type++) { + switch (self->symtab_type) { + case SYMTAB__BUILD_ID_CACHE: /* skip the locally configured cache if a symfs is given */ if (symbol_conf.symfs[0] || (dso__build_id_filename(self, name, size) == NULL)) { continue; } break; - case DSO__ORIG_FEDORA: + case SYMTAB__FEDORA_DEBUGINFO: snprintf(name, size, "%s/usr/lib/debug%s.debug", symbol_conf.symfs, self->long_name); break; - case DSO__ORIG_UBUNTU: + case SYMTAB__UBUNTU_DEBUGINFO: snprintf(name, size, "%s/usr/lib/debug%s", symbol_conf.symfs, self->long_name); break; - case DSO__ORIG_BUILDID: { + case SYMTAB__BUILDID_DEBUGINFO: { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; if (!self->has_build_id) @@ -1519,11 +1519,11 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) symbol_conf.symfs, build_id_hex, build_id_hex + 2); } break; - case DSO__ORIG_DSO: + case SYMTAB__SYSTEM_PATH_DSO: snprintf(name, size, "%s%s", symbol_conf.symfs, self->long_name); break; - case DSO__ORIG_GUEST_KMODULE: + case SYMTAB__GUEST_KMODULE: if (map->groups && machine) root_dir = machine->root_dir; else @@ -1532,7 +1532,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) root_dir, self->long_name); break; - case DSO__ORIG_KMODULE: + case SYMTAB__SYSTEM_PATH_KMODULE: snprintf(name, size, "%s%s", symbol_conf.symfs, self->long_name); break; @@ -1544,7 +1544,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) */ if (want_symtab) { want_symtab = 0; - self->origin = DSO__ORIG_BUILD_ID_CACHE; + self->symtab_type = SYMTAB__BUILD_ID_CACHE; } else continue; } @@ -1757,9 +1757,9 @@ struct map *machine__new_module(struct machine *self, u64 start, return NULL; if (machine__is_host(self)) - dso->origin = DSO__ORIG_KMODULE; + dso->symtab_type = SYMTAB__SYSTEM_PATH_KMODULE; else - dso->origin = DSO__ORIG_GUEST_KMODULE; + dso->symtab_type = SYMTAB__GUEST_KMODULE; map_groups__insert(&self->kmaps, map); return map; } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 4d7ed09fe332..713b0b40cc4a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -48,12 +48,17 @@ char *strxfrchar(char *s, char from, char to); #define BUILD_ID_SIZE 20 +/** struct symbol - symtab entry + * + * @ignore - resolvable but tools ignore it (e.g. idle routines) + */ struct symbol { struct rb_node rb_node; u64 start; u64 end; u16 namelen; u8 binding; + bool ignore; char name[0]; }; @@ -137,7 +142,7 @@ struct dso { u8 annotate_warned:1; u8 sname_alloc:1; u8 lname_alloc:1; - unsigned char origin; + unsigned char symtab_type; u8 sorted_by_name; u8 loaded; u8 build_id[BUILD_ID_SIZE]; @@ -188,18 +193,18 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); -enum dso_origin { - DSO__ORIG_KERNEL = 0, - DSO__ORIG_GUEST_KERNEL, - DSO__ORIG_JAVA_JIT, - DSO__ORIG_BUILD_ID_CACHE, - DSO__ORIG_FEDORA, - DSO__ORIG_UBUNTU, - DSO__ORIG_BUILDID, - DSO__ORIG_DSO, - DSO__ORIG_GUEST_KMODULE, - DSO__ORIG_KMODULE, - DSO__ORIG_NOT_FOUND, +enum symtab_type { + SYMTAB__KALLSYMS = 0, + SYMTAB__GUEST_KALLSYMS, + SYMTAB__JAVA_JIT, + SYMTAB__BUILD_ID_CACHE, + SYMTAB__FEDORA_DEBUGINFO, + SYMTAB__UBUNTU_DEBUGINFO, + SYMTAB__BUILDID_DEBUGINFO, + SYMTAB__SYSTEM_PATH_DSO, + SYMTAB__GUEST_KMODULE, + SYMTAB__SYSTEM_PATH_KMODULE, + SYMTAB__NOT_FOUND, }; char dso__symtab_origin(const struct dso *self); diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 75cfe4d45119..a11f60735a18 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -171,7 +171,7 @@ float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) { struct sym_entry *syme, *n; float sum_ksamples = 0.0; - int snap = !top->display_weighted ? top->sym_counter : 0, j; + int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j; /* Sort the active symbols */ pthread_mutex_lock(&top->active_symbols_lock); @@ -184,9 +184,9 @@ float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) if (syme->snap_count != 0) { if ((top->hide_user_symbols && - syme->origin == PERF_RECORD_MISC_USER) || + syme->map->dso->kernel == DSO_TYPE_USER) || (top->hide_kernel_symbols && - syme->origin == PERF_RECORD_MISC_KERNEL)) { + syme->map->dso->kernel == DSO_TYPE_KERNEL)) { perf_top__remove_active_sym(top, syme); continue; } diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 96d1cb78af01..bfbf95bcc603 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -16,8 +16,6 @@ struct sym_entry { struct list_head node; unsigned long snap_count; double weight; - int skip; - u8 origin; struct map *map; unsigned long count[0]; }; @@ -41,7 +39,7 @@ struct perf_top { u64 exact_samples; u64 guest_us_samples, guest_kernel_samples; int print_entries, count_filter, delay_secs; - int display_weighted, freq, rb_entries, sym_counter; + int display_weighted, freq, rb_entries; pid_t target_pid, target_tid; bool hide_kernel_symbols, hide_user_symbols, zero; const char *cpu_list; diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index d8e622dd738a..0a7ed5b5e281 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -2643,68 +2643,13 @@ static void print_lat_fmt(void *data, int size __unused) printf("."); if (lock_depth < 0) - printf("."); + printf(". "); else - printf("%d", lock_depth); + printf("%d ", lock_depth); } -/* taken from Linux, written by Frederic Weisbecker */ -static void print_graph_cpu(int cpu) -{ - int i; - int log10_this = log10_cpu(cpu); - int log10_all = log10_cpu(cpus); - - - /* - * Start with a space character - to make it stand out - * to the right a bit when trace output is pasted into - * email: - */ - printf(" "); - - /* - * Tricky - we space the CPU field according to the max - * number of online CPUs. On a 2-cpu system it would take - * a maximum of 1 digit - on a 128 cpu system it would - * take up to 3 digits: - */ - for (i = 0; i < log10_all - log10_this; i++) - printf(" "); - - printf("%d) ", cpu); -} - -#define TRACE_GRAPH_PROCINFO_LENGTH 14 #define TRACE_GRAPH_INDENT 2 -static void print_graph_proc(int pid, const char *comm) -{ - /* sign + log10(MAX_INT) + '\0' */ - char pid_str[11]; - int spaces = 0; - int len; - int i; - - sprintf(pid_str, "%d", pid); - - /* 1 stands for the "-" character */ - len = strlen(comm) + strlen(pid_str) + 1; - - if (len < TRACE_GRAPH_PROCINFO_LENGTH) - spaces = TRACE_GRAPH_PROCINFO_LENGTH - len; - - /* First spaces to align center */ - for (i = 0; i < spaces / 2; i++) - printf(" "); - - printf("%s-%s", comm, pid_str); - - /* Last spaces to align center */ - for (i = 0; i < spaces - (spaces / 2); i++) - printf(" "); -} - static struct record * get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, struct record *next) @@ -2876,21 +2821,13 @@ static void print_graph_nested(struct event *event, void *data) static void pretty_print_func_ent(void *data, int size, struct event *event, - int cpu, int pid, const char *comm, - unsigned long secs, unsigned long usecs) + int cpu, int pid) { struct format_field *field; struct record *rec; void *copy_data; unsigned long val; - printf("%5lu.%06lu | ", secs, usecs); - - print_graph_cpu(cpu); - print_graph_proc(pid, comm); - - printf(" | "); - if (latency_format) { print_lat_fmt(data, size); printf(" | "); @@ -2923,22 +2860,13 @@ out_free: } static void -pretty_print_func_ret(void *data, int size __unused, struct event *event, - int cpu, int pid, const char *comm, - unsigned long secs, unsigned long usecs) +pretty_print_func_ret(void *data, int size __unused, struct event *event) { unsigned long long rettime, calltime; unsigned long long duration, depth; struct format_field *field; int i; - printf("%5lu.%06lu | ", secs, usecs); - - print_graph_cpu(cpu); - print_graph_proc(pid, comm); - - printf(" | "); - if (latency_format) { print_lat_fmt(data, size); printf(" | "); @@ -2976,31 +2904,21 @@ pretty_print_func_ret(void *data, int size __unused, struct event *event, static void pretty_print_func_graph(void *data, int size, struct event *event, - int cpu, int pid, const char *comm, - unsigned long secs, unsigned long usecs) + int cpu, int pid) { if (event->flags & EVENT_FL_ISFUNCENT) - pretty_print_func_ent(data, size, event, - cpu, pid, comm, secs, usecs); + pretty_print_func_ent(data, size, event, cpu, pid); else if (event->flags & EVENT_FL_ISFUNCRET) - pretty_print_func_ret(data, size, event, - cpu, pid, comm, secs, usecs); + pretty_print_func_ret(data, size, event); printf("\n"); } -void print_event(int cpu, void *data, int size, unsigned long long nsecs, - char *comm) +void print_trace_event(int cpu, void *data, int size) { struct event *event; - unsigned long secs; - unsigned long usecs; int type; int pid; - secs = nsecs / NSECS_PER_SEC; - nsecs -= secs * NSECS_PER_SEC; - usecs = nsecs / NSECS_PER_USEC; - type = trace_parse_common_type(data); event = trace_find_event(type); @@ -3012,17 +2930,10 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, pid = trace_parse_common_pid(data); if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) - return pretty_print_func_graph(data, size, event, cpu, - pid, comm, secs, usecs); + return pretty_print_func_graph(data, size, event, cpu, pid); - if (latency_format) { - printf("%8.8s-%-5d %3d", - comm, pid, cpu); + if (latency_format) print_lat_fmt(data, size); - } else - printf("%16s-%-5d [%03d]", comm, pid, cpu); - - printf(" %5lu.%06lu: %s: ", secs, usecs, event->name); if (event->flags & EVENT_FL_FAILED) { printf("EVENT '%s' FAILED TO PARSE\n", @@ -3031,7 +2942,6 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, } pretty_print(data, size, event); - printf("\n"); } static void print_fields(struct print_flag_sym *field) diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index f7af2fca965d..66f4b78737ab 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -36,11 +36,10 @@ static int stop_script_unsupported(void) return 0; } -static void process_event_unsupported(int cpu __unused, - void *data __unused, - int size __unused, - unsigned long long nsecs __unused, - char *comm __unused) +static void process_event_unsupported(union perf_event *event __unused, + struct perf_sample *sample __unused, + struct perf_session *session __unused, + struct thread *thread __unused) { } diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index b5f12ca24d99..b04da5722437 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -3,6 +3,7 @@ #include <stdbool.h> #include "parse-events.h" +#include "session.h" #define __unused __attribute__((unused)) @@ -176,8 +177,7 @@ void print_printk(void); int parse_ftrace_file(char *buf, unsigned long size); int parse_event_file(char *buf, unsigned long size, char *sys); -void print_event(int cpu, void *data, int size, unsigned long long nsecs, - char *comm); +void print_trace_event(int cpu, void *data, int size); extern int file_bigendian; extern int host_bigendian; @@ -278,8 +278,10 @@ struct scripting_ops { const char *name; int (*start_script) (const char *script, int argc, const char **argv); int (*stop_script) (void); - void (*process_event) (int cpu, void *data, int size, - unsigned long long nsecs, char *comm); + void (*process_event) (union perf_event *event, + struct perf_sample *sample, + struct perf_session *session, + struct thread *thread); int (*generate_script) (const char *outfile); }; diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index ba7c63af6f3b..8ce792ea08e9 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -37,6 +37,8 @@ $default{"POWEROFF_ON_SUCCESS"} = 0; $default{"BUILD_OPTIONS"} = ""; $default{"BISECT_SLEEP_TIME"} = 60; # sleep time between bisects $default{"CLEAR_LOG"} = 0; +$default{"BISECT_MANUAL"} = 0; +$default{"BISECT_SKIP"} = 1; $default{"SUCCESS_LINE"} = "login:"; $default{"BOOTED_TIMEOUT"} = 1; $default{"DIE_ON_FAILURE"} = 1; @@ -45,6 +47,7 @@ $default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE"; $default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot"; $default{"STOP_AFTER_SUCCESS"} = 10; $default{"STOP_AFTER_FAILURE"} = 60; +$default{"STOP_TEST_AFTER"} = 600; $default{"LOCALVERSION"} = "-test"; my $ktest_config; @@ -81,6 +84,8 @@ my $addconfig; my $in_bisect = 0; my $bisect_bad = ""; my $reverse_bisect; +my $bisect_manual; +my $bisect_skip; my $in_patchcheck = 0; my $run_test; my $redirect; @@ -98,6 +103,7 @@ my $console; my $success_line; my $stop_after_success; my $stop_after_failure; +my $stop_test_after; my $build_target; my $target_image; my $localversion; @@ -462,6 +468,10 @@ sub dodie { `$power_off`; } + if (defined($opt{"LOG_FILE"})) { + print " See $opt{LOG_FILE} for more info.\n"; + } + die @_, "\n"; } @@ -760,8 +770,10 @@ sub monitor { my $success_start; my $failure_start; + my $monitor_start = time; + my $done = 0; - for (;;) { + while (!$done) { if ($booted) { $line = wait_for_input($monitor_fp, $booted_timeout); @@ -796,7 +808,7 @@ sub monitor { } if ($full_line =~ /call trace:/i) { - if (!$skip_call_trace) { + if (!$bug && !$skip_call_trace) { $bug = 1; $failure_start = time; } @@ -816,12 +828,19 @@ sub monitor { } if ($full_line =~ /Kernel panic -/) { + $failure_start = time; $bug = 1; } if ($line =~ /\n/) { $full_line = ""; } + + if ($stop_test_after > 0 && !$booted && !$bug) { + if (time - $monitor_start > $stop_test_after) { + $done = 1; + } + } } close(DMESG); @@ -925,6 +944,18 @@ sub check_buildlog { return 1; } +sub make_oldconfig { + my ($defconfig) = @_; + + if (!run_command "$defconfig $make oldnoconfig") { + # Perhaps oldnoconfig doesn't exist in this version of the kernel + # try a yes '' | oldconfig + doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; + run_command "yes '' | $defconfig $make oldconfig" or + dodie "failed make config oldconfig"; + } +} + sub build { my ($type) = @_; my $defconfig = ""; @@ -970,8 +1001,12 @@ sub build { $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; } - run_command "$defconfig $make $type" or - dodie "failed make config"; + if ($type eq "oldnoconfig") { + make_oldconfig $defconfig; + } else { + run_command "$defconfig $make $type" or + dodie "failed make config"; + } $redirect = "$buildlog"; if (!run_command "$make $build_options") { @@ -1025,6 +1060,21 @@ sub get_version { doprint "$version\n"; } +sub answer_bisect { + for (;;) { + doprint "Pass or fail? [p/f]"; + my $ans = <STDIN>; + chomp $ans; + if ($ans eq "p" || $ans eq "P") { + return 1; + } elsif ($ans eq "f" || $ans eq "F") { + return 0; + } else { + print "Please answer 'P' or 'F'\n"; + } + } +} + sub child_run_test { my $failed = 0; @@ -1070,6 +1120,7 @@ sub do_run_test { # we are not guaranteed to get a full line $full_line .= $line; + doprint $line; if ($full_line =~ /call trace:/i) { $bug = 1; @@ -1086,6 +1137,19 @@ sub do_run_test { } while (!$child_done && !$bug); if ($bug) { + my $failure_start = time; + my $now; + do { + $line = wait_for_input($monitor_fp, 1); + if (defined($line)) { + doprint $line; + } + $now = time; + if ($now - $failure_start >= $stop_after_failure) { + last; + } + } while (defined($line)); + doprint "Detected kernel crash!\n"; # kill the child with extreme prejudice kill 9, $child_pid; @@ -1131,7 +1195,15 @@ sub run_git_bisect { return 1; } -# returns 1 on success, 0 on failure +sub bisect_reboot { + doprint "Reboot and sleep $bisect_sleep_time seconds\n"; + reboot; + start_monitor; + wait_for_monitor $bisect_sleep_time; + end_monitor; +} + +# returns 1 on success, 0 on failure, -1 on skip sub run_bisect_test { my ($type, $buildtype) = @_; @@ -1145,6 +1217,10 @@ sub run_bisect_test { build $buildtype or $failed = 1; if ($type ne "build") { + if ($failed && $bisect_skip) { + $in_bisect = 0; + return -1; + } dodie "Failed on build" if $failed; # Now boot the box @@ -1156,6 +1232,12 @@ sub run_bisect_test { monitor or $failed = 1; if ($type ne "boot") { + if ($failed && $bisect_skip) { + end_monitor; + bisect_reboot; + $in_bisect = 0; + return -1; + } dodie "Failed on boot" if $failed; do_run_test or $failed = 1; @@ -1168,11 +1250,7 @@ sub run_bisect_test { # reboot the box to a good kernel if ($type ne "build") { - doprint "Reboot and sleep $bisect_sleep_time seconds\n"; - reboot; - start_monitor; - wait_for_monitor $bisect_sleep_time; - end_monitor; + bisect_reboot; } } else { $result = 1; @@ -1193,16 +1271,22 @@ sub run_bisect { my $ret = run_bisect_test $type, $buildtype; + if ($bisect_manual) { + $ret = answer_bisect; + } # Are we looking for where it worked, not failed? if ($reverse_bisect) { $ret = !$ret; } - if ($ret) { + if ($ret > 0) { return "good"; - } else { + } elsif ($ret == 0) { return "bad"; + } elsif ($bisect_skip) { + doprint "HIT A BAD COMMIT ... SKIPPING\n"; + return "skip"; } } @@ -1220,6 +1304,13 @@ sub bisect { my $type = $opt{"BISECT_TYPE[$i]"}; my $start = $opt{"BISECT_START[$i]"}; my $replay = $opt{"BISECT_REPLAY[$i]"}; + my $start_files = $opt{"BISECT_FILES[$i]"}; + + if (defined($start_files)) { + $start_files = " -- " . $start_files; + } else { + $start_files = ""; + } # convert to true sha1's $good = get_sha1($good); @@ -1273,7 +1364,7 @@ sub bisect { die "Failed to checkout $head"; } - run_command "git bisect start" or + run_command "git bisect start$start_files" or dodie "could not start bisect"; run_command "git bisect good $good" or @@ -1390,9 +1481,7 @@ sub create_config { close(OUT); # exit; - run_command "$make oldnoconfig" or - dodie "failed make config oldconfig"; - + make_oldconfig ""; } sub compare_configs { @@ -1505,7 +1594,9 @@ sub run_config_bisect { } $ret = run_config_bisect_test $type; - + if ($bisect_manual) { + $ret = answer_bisect; + } if ($ret) { process_passed %current_config; return 0; @@ -1536,7 +1627,13 @@ sub run_config_bisect { $half = int($#start_list / 2); } while ($half > 0); - # we found a single config, try it again + # we found a single config, try it again unless we are running manually + + if ($bisect_manual) { + process_failed $start_list[0]; + return 1; + } + my @tophalf = @start_list[0 .. 0]; $ret = run_config_bisect_test $type; @@ -1594,8 +1691,7 @@ sub config_bisect { close(IN); # Now run oldconfig with the minconfig (and addconfigs) - run_command "$defconfig $make oldnoconfig" or - dodie "failed make config oldconfig"; + make_oldconfig $defconfig; # check to see what we lost (or gained) open (IN, $output_config) @@ -1907,6 +2003,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $poweroff_after_halt = set_test_option("POWEROFF_AFTER_HALT", $i); $sleep_time = set_test_option("SLEEP_TIME", $i); $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); + $bisect_manual = set_test_option("BISECT_MANUAL", $i); + $bisect_skip = set_test_option("BISECT_SKIP", $i); $store_failures = set_test_option("STORE_FAILURES", $i); $timeout = set_test_option("TIMEOUT", $i); $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); @@ -1914,6 +2012,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $success_line = set_test_option("SUCCESS_LINE", $i); $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i); $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i); + $stop_test_after = set_test_option("STOP_TEST_AFTER", $i); $build_target = set_test_option("BUILD_TARGET", $i); $ssh_exec = set_test_option("SSH_EXEC", $i); $scp_to_target = set_test_option("SCP_TO_TARGET", $i); diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 3408c594b2de..4c5d6bd74a02 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -306,6 +306,14 @@ # (default 60) #STOP_AFTER_FAILURE = 60 +# In case the console constantly fills the screen, having +# a specified time to stop the test if it never succeeds nor fails +# is recommended. +# Note: this is ignored if a success or failure is detected. +# (in seconds) +# (default 600, -1 is to never stop) +#STOP_TEST_AFTER = 600 + # Stop testing if a build fails. If set, the script will end if # a failure is detected, otherwise it will save off the .config, # dmesg and bootlog in a directory called @@ -519,6 +527,24 @@ # git bisect good, git bisect bad, and running the git bisect replay # if the BISECT_REPLAY is set. # +# BISECT_SKIP = 1 (optional, default 0) +# +# If BISECT_TYPE is set to test but the build fails, ktest will +# simply fail the test and end their. You could use BISECT_REPLAY +# and BISECT_START to resume after you found a new starting point, +# or you could set BISECT_SKIP to 1. If BISECT_SKIP is set to 1, +# when something other than the BISECT_TYPE fails, ktest.pl will +# run "git bisect skip" and try again. +# +# BISECT_FILES = <path> (optional, default undefined) +# +# To just run the git bisect on a specific path, set BISECT_FILES. +# For example: +# +# BISECT_FILES = arch/x86 kernel/time +# +# Will run the bisect with "git bisect start -- arch/x86 kernel/time" +# # BISECT_REVERSE = 1 (optional, default 0) # # In those strange instances where it was broken forever @@ -528,6 +554,15 @@ # With BISECT_REVERSE = 1, The test will consider failures as # good, and success as bad. # +# BISECT_MANUAL = 1 (optional, default 0) +# +# In case there's a problem with automating the bisect for +# whatever reason. (Can't reboot, want to inspect each iteration) +# Doing a BISECT_MANUAL will have the test wait for you to +# tell it if the test passed or failed after each iteration. +# This is basicall the same as running git bisect yourself +# but ktest will rebuild and install the kernel for you. +# # BISECT_CHECK = 1 (optional, default 0) # # Just to be sure the good is good and bad is bad, setting @@ -613,10 +648,17 @@ # # CONFIG_BISECT is the config that failed to boot # +# If BISECT_MANUAL is set, it will pause between iterations. +# This is useful to use just ktest.pl just for the config bisect. +# If you set it to build, it will run the bisect and you can +# control what happens in between iterations. It will ask you if +# the test succeeded or not and continue the config bisect. +# # Example: # TEST_START # TEST_TYPE = config_bisect # CONFIG_BISECT_TYPE = build # CONFIG_BISECT = /home/test/˘onfig-bad # MIN_CONFIG = /home/test/config-min +# BISECT_MANUAL = 1 # |