summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2015-03-24 16:22:44 (GMT)
committerIngo Molnar <mingo@kernel.org>2015-03-24 16:22:44 (GMT)
commitbaa5a7bc5dd069bb37de9c8bdb5ea7f4e2e939e9 (patch)
tree50864d78ede1a333b0b6fb8fe09486ed3f3ad1c2
parent963a70b8a2d65538f7d58b2b84a2ae10a3ecb6ea (diff)
parente03eaa400cf8b8bded86cc5c41018a1c69152f16 (diff)
downloadlinux-baa5a7bc5dd069bb37de9c8bdb5ea7f4e2e939e9.tar.xz
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: User visible changes: - Improve support of compressed kernel modules (Jiri Olsa) - Add --kallsyms option to 'perf diff' (David Ahern) - Add pid/tid filtering to 'report' and 'script' commands (David Ahern) - Add support for __print_array() in libtraceevent (Javi Merino) - Save DSO loading errno to better report errors (Arnaldo Carvalho de Melo) - Fix 'probe' to get ummapped symbol address on kernel (Masami Hiramatsu) - Print big numbers using thousands' group in 'kmem' (Namhyung Kim) - Remove (null) value of "Sort order" for perf mem report (Yunlong Song) Infrastructure changes: - Handle NULL comm name in libtracevent (Josef Bacik) - Libtraceevent synchronization with trace-cmd repo (Steven Rostedt) - Work around lack of sched_getcpu() in glibc < 2.6. (Vinson Lee) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--tools/lib/traceevent/event-parse.c267
-rw-r--r--tools/lib/traceevent/event-parse.h21
-rw-r--r--tools/lib/traceevent/event-plugin.c60
-rw-r--r--tools/lib/traceevent/kbuffer-parse.c12
-rw-r--r--tools/lib/traceevent/kbuffer.h1
-rw-r--r--tools/lib/traceevent/parse-filter.c2
-rw-r--r--tools/perf/Documentation/perf-diff.txt3
-rw-r--r--tools/perf/Documentation/perf-report.txt5
-rw-r--r--tools/perf/Documentation/perf-script.txt6
-rw-r--r--tools/perf/builtin-diff.c2
-rw-r--r--tools/perf/builtin-kmem.c11
-rw-r--r--tools/perf/builtin-report.c6
-rw-r--r--tools/perf/builtin-script.c4
-rw-r--r--tools/perf/builtin-top.c6
-rw-r--r--tools/perf/util/annotate.c32
-rw-r--r--tools/perf/util/cloexec.c6
-rw-r--r--tools/perf/util/cloexec.h6
-rw-r--r--tools/perf/util/dso.c61
-rw-r--r--tools/perf/util/dso.h31
-rw-r--r--tools/perf/util/header.c2
-rw-r--r--tools/perf/util/machine.c72
-rw-r--r--tools/perf/util/probe-event.c5
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c5
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c5
-rw-r--r--tools/perf/util/sort.h1
-rw-r--r--tools/perf/util/symbol-elf.c57
-rw-r--r--tools/perf/util/symbol-minimal.c7
-rw-r--r--tools/perf/util/symbol.c31
-rw-r--r--tools/perf/util/symbol.h7
-rw-r--r--tools/perf/util/target.c7
-rw-r--r--tools/perf/util/thread.h11
31 files changed, 621 insertions, 131 deletions
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
index d7c37a7..b6d11ee 100644
--- a/tools/lib/traceevent/event-parse.c
+++ b/tools/lib/traceevent/event-parse.c
@@ -304,7 +304,10 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
if (!item)
return -1;
- item->comm = strdup(comm);
+ if (comm)
+ item->comm = strdup(comm);
+ else
+ item->comm = strdup("<...>");
if (!item->comm) {
free(item);
return -1;
@@ -318,9 +321,14 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
return 0;
}
-void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock)
+int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock)
{
- pevent->trace_clock = trace_clock;
+ pevent->trace_clock = strdup(trace_clock);
+ if (!pevent->trace_clock) {
+ errno = ENOMEM;
+ return -1;
+ }
+ return 0;
}
struct func_map {
@@ -758,6 +766,11 @@ static void free_arg(struct print_arg *arg)
free_arg(arg->hex.field);
free_arg(arg->hex.size);
break;
+ case PRINT_INT_ARRAY:
+ free_arg(arg->int_array.field);
+ free_arg(arg->int_array.count);
+ free_arg(arg->int_array.el_size);
+ break;
case PRINT_TYPE:
free(arg->typecast.type);
free_arg(arg->typecast.item);
@@ -2014,6 +2027,38 @@ process_entry(struct event_format *event __maybe_unused, struct print_arg *arg,
return EVENT_ERROR;
}
+static int alloc_and_process_delim(struct event_format *event, char *next_token,
+ struct print_arg **print_arg)
+{
+ struct print_arg *field;
+ enum event_type type;
+ char *token;
+ int ret = 0;
+
+ field = alloc_arg();
+ if (!field) {
+ do_warning_event(event, "%s: not enough memory!", __func__);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ type = process_arg(event, field, &token);
+
+ if (test_type_token(type, token, EVENT_DELIM, next_token)) {
+ errno = EINVAL;
+ ret = -1;
+ free_arg(field);
+ goto out_free_token;
+ }
+
+ *print_arg = field;
+
+out_free_token:
+ free_token(token);
+
+ return ret;
+}
+
static char *arg_eval (struct print_arg *arg);
static unsigned long long
@@ -2486,49 +2531,46 @@ out_free:
static enum event_type
process_hex(struct event_format *event, struct print_arg *arg, char **tok)
{
- struct print_arg *field;
- enum event_type type;
- char *token = NULL;
-
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_HEX;
- field = alloc_arg();
- if (!field) {
- do_warning_event(event, "%s: not enough memory!", __func__);
- goto out_free;
- }
-
- type = process_arg(event, field, &token);
+ if (alloc_and_process_delim(event, ",", &arg->hex.field))
+ goto out;
- if (test_type_token(type, token, EVENT_DELIM, ","))
- goto out_free;
+ if (alloc_and_process_delim(event, ")", &arg->hex.size))
+ goto free_field;
- arg->hex.field = field;
+ return read_token_item(tok);
- free_token(token);
+free_field:
+ free_arg(arg->hex.field);
+out:
+ *tok = NULL;
+ return EVENT_ERROR;
+}
- field = alloc_arg();
- if (!field) {
- do_warning_event(event, "%s: not enough memory!", __func__);
- *tok = NULL;
- return EVENT_ERROR;
- }
+static enum event_type
+process_int_array(struct event_format *event, struct print_arg *arg, char **tok)
+{
+ memset(arg, 0, sizeof(*arg));
+ arg->type = PRINT_INT_ARRAY;
- type = process_arg(event, field, &token);
+ if (alloc_and_process_delim(event, ",", &arg->int_array.field))
+ goto out;
- if (test_type_token(type, token, EVENT_DELIM, ")"))
- goto out_free;
+ if (alloc_and_process_delim(event, ",", &arg->int_array.count))
+ goto free_field;
- arg->hex.size = field;
+ if (alloc_and_process_delim(event, ")", &arg->int_array.el_size))
+ goto free_size;
- free_token(token);
- type = read_token_item(tok);
- return type;
+ return read_token_item(tok);
- out_free:
- free_arg(field);
- free_token(token);
+free_size:
+ free_arg(arg->int_array.count);
+free_field:
+ free_arg(arg->int_array.field);
+out:
*tok = NULL;
return EVENT_ERROR;
}
@@ -2828,6 +2870,10 @@ process_function(struct event_format *event, struct print_arg *arg,
free_token(token);
return process_hex(event, arg, tok);
}
+ if (strcmp(token, "__print_array") == 0) {
+ free_token(token);
+ return process_int_array(event, arg, tok);
+ }
if (strcmp(token, "__get_str") == 0) {
free_token(token);
return process_str(event, arg, tok);
@@ -3356,6 +3402,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
break;
case PRINT_FLAGS:
case PRINT_SYMBOL:
+ case PRINT_INT_ARRAY:
case PRINT_HEX:
break;
case PRINT_TYPE:
@@ -3766,6 +3813,54 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
}
break;
+ case PRINT_INT_ARRAY: {
+ void *num;
+ int el_size;
+
+ if (arg->int_array.field->type == PRINT_DYNAMIC_ARRAY) {
+ unsigned long offset;
+ struct format_field *field =
+ arg->int_array.field->dynarray.field;
+ offset = pevent_read_number(pevent,
+ data + field->offset,
+ field->size);
+ num = data + (offset & 0xffff);
+ } else {
+ field = arg->int_array.field->field.field;
+ if (!field) {
+ str = arg->int_array.field->field.name;
+ field = pevent_find_any_field(event, str);
+ if (!field)
+ goto out_warning_field;
+ arg->int_array.field->field.field = field;
+ }
+ num = data + field->offset;
+ }
+ len = eval_num_arg(data, size, event, arg->int_array.count);
+ el_size = eval_num_arg(data, size, event,
+ arg->int_array.el_size);
+ for (i = 0; i < len; i++) {
+ if (i)
+ trace_seq_putc(s, ' ');
+
+ if (el_size == 1) {
+ trace_seq_printf(s, "%u", *(uint8_t *)num);
+ } else if (el_size == 2) {
+ trace_seq_printf(s, "%u", *(uint16_t *)num);
+ } else if (el_size == 4) {
+ trace_seq_printf(s, "%u", *(uint32_t *)num);
+ } else if (el_size == 8) {
+ trace_seq_printf(s, "%lu", *(uint64_t *)num);
+ } else {
+ trace_seq_printf(s, "BAD SIZE:%d 0x%x",
+ el_size, *(uint8_t *)num);
+ el_size = 1;
+ }
+
+ num += el_size;
+ }
+ break;
+ }
case PRINT_TYPE:
break;
case PRINT_STRING: {
@@ -3997,6 +4092,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
goto process_again;
case '.':
goto process_again;
+ case 'z':
+ case 'Z':
+ ls = 1;
+ goto process_again;
case 'p':
ls = 1;
/* fall through */
@@ -4939,6 +5038,96 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid)
return comm;
}
+static struct cmdline *
+pid_from_cmdlist(struct pevent *pevent, const char *comm, struct cmdline *next)
+{
+ struct cmdline_list *cmdlist = (struct cmdline_list *)next;
+
+ if (cmdlist)
+ cmdlist = cmdlist->next;
+ else
+ cmdlist = pevent->cmdlist;
+
+ while (cmdlist && strcmp(cmdlist->comm, comm) != 0)
+ cmdlist = cmdlist->next;
+
+ return (struct cmdline *)cmdlist;
+}
+
+/**
+ * pevent_data_pid_from_comm - return the pid from a given comm
+ * @pevent: a handle to the pevent
+ * @comm: the cmdline to find the pid from
+ * @next: the cmdline structure to find the next comm
+ *
+ * This returns the cmdline structure that holds a pid for a given
+ * comm, or NULL if none found. As there may be more than one pid for
+ * a given comm, the result of this call can be passed back into
+ * a recurring call in the @next paramater, and then it will find the
+ * next pid.
+ * Also, it does a linear seach, so it may be slow.
+ */
+struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm,
+ struct cmdline *next)
+{
+ struct cmdline *cmdline;
+
+ /*
+ * If the cmdlines have not been converted yet, then use
+ * the list.
+ */
+ if (!pevent->cmdlines)
+ return pid_from_cmdlist(pevent, comm, next);
+
+ if (next) {
+ /*
+ * The next pointer could have been still from
+ * a previous call before cmdlines were created
+ */
+ if (next < pevent->cmdlines ||
+ next >= pevent->cmdlines + pevent->cmdline_count)
+ next = NULL;
+ else
+ cmdline = next++;
+ }
+
+ if (!next)
+ cmdline = pevent->cmdlines;
+
+ while (cmdline < pevent->cmdlines + pevent->cmdline_count) {
+ if (strcmp(cmdline->comm, comm) == 0)
+ return cmdline;
+ cmdline++;
+ }
+ return NULL;
+}
+
+/**
+ * pevent_cmdline_pid - return the pid associated to a given cmdline
+ * @cmdline: The cmdline structure to get the pid from
+ *
+ * Returns the pid for a give cmdline. If @cmdline is NULL, then
+ * -1 is returned.
+ */
+int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline)
+{
+ struct cmdline_list *cmdlist = (struct cmdline_list *)cmdline;
+
+ if (!cmdline)
+ return -1;
+
+ /*
+ * If cmdlines have not been created yet, or cmdline is
+ * not part of the array, then treat it as a cmdlist instead.
+ */
+ if (!pevent->cmdlines ||
+ cmdline < pevent->cmdlines ||
+ cmdline >= pevent->cmdlines + pevent->cmdline_count)
+ return cmdlist->pid;
+
+ return cmdline->pid;
+}
+
/**
* pevent_data_comm_from_pid - parse the data into the print format
* @s: the trace_seq to write to
@@ -5256,6 +5445,15 @@ static void print_args(struct print_arg *args)
print_args(args->hex.size);
printf(")");
break;
+ case PRINT_INT_ARRAY:
+ printf("__print_array(");
+ print_args(args->int_array.field);
+ printf(", ");
+ print_args(args->int_array.count);
+ printf(", ");
+ print_args(args->int_array.el_size);
+ printf(")");
+ break;
case PRINT_STRING:
case PRINT_BSTRING:
printf("__get_str(%s)", args->string.string);
@@ -6346,6 +6544,7 @@ void pevent_free(struct pevent *pevent)
free_handler(handle);
}
+ free(pevent->trace_clock);
free(pevent->events);
free(pevent->sort_events);
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h
index 6abda54..86a5839 100644
--- a/tools/lib/traceevent/event-parse.h
+++ b/tools/lib/traceevent/event-parse.h
@@ -116,7 +116,7 @@ struct pevent_plugin_option {
char *name;
char *plugin_alias;
char *description;
- char *value;
+ const char *value;
void *priv;
int set;
};
@@ -154,6 +154,10 @@ struct pevent_plugin_option {
* .plugin_alias is used to give a shorter name to access
* the vairable. Useful if a plugin handles more than one event.
*
+ * If .value is not set, then it is considered a boolean and only
+ * .set will be processed. If .value is defined, then it is considered
+ * a string option and .set will be ignored.
+ *
* PEVENT_PLUGIN_ALIAS: (optional)
* The name to use for finding options (uses filename if not defined)
*/
@@ -247,6 +251,12 @@ struct print_arg_hex {
struct print_arg *size;
};
+struct print_arg_int_array {
+ struct print_arg *field;
+ struct print_arg *count;
+ struct print_arg *el_size;
+};
+
struct print_arg_dynarray {
struct format_field *field;
struct print_arg *index;
@@ -275,6 +285,7 @@ enum print_arg_type {
PRINT_FLAGS,
PRINT_SYMBOL,
PRINT_HEX,
+ PRINT_INT_ARRAY,
PRINT_TYPE,
PRINT_STRING,
PRINT_BSTRING,
@@ -294,6 +305,7 @@ struct print_arg {
struct print_arg_flags flags;
struct print_arg_symbol symbol;
struct print_arg_hex hex;
+ struct print_arg_int_array int_array;
struct print_arg_func func;
struct print_arg_string string;
struct print_arg_bitmask bitmask;
@@ -599,7 +611,7 @@ enum trace_flag_type {
};
int pevent_register_comm(struct pevent *pevent, const char *comm, int pid);
-void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock);
+int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock);
int pevent_register_function(struct pevent *pevent, char *name,
unsigned long long addr, char *mod);
int pevent_register_print_string(struct pevent *pevent, const char *fmt,
@@ -678,6 +690,11 @@ int pevent_data_type(struct pevent *pevent, struct pevent_record *rec);
struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid);
+struct cmdline;
+struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm,
+ struct cmdline *next);
+int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline);
+
void pevent_event_info(struct trace_seq *s, struct event_format *event,
struct pevent_record *record);
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
diff --git a/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c
index 136162c..a16756a 100644
--- a/tools/lib/traceevent/event-plugin.c
+++ b/tools/lib/traceevent/event-plugin.c
@@ -18,6 +18,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
@@ -49,6 +50,52 @@ struct plugin_list {
void *handle;
};
+static void lower_case(char *str)
+{
+ if (!str)
+ return;
+ for (; *str; str++)
+ *str = tolower(*str);
+}
+
+static int update_option_value(struct pevent_plugin_option *op, const char *val)
+{
+ char *op_val;
+
+ if (!val) {
+ /* toggle, only if option is boolean */
+ if (op->value)
+ /* Warn? */
+ return 0;
+ op->set ^= 1;
+ return 0;
+ }
+
+ /*
+ * If the option has a value then it takes a string
+ * otherwise the option is a boolean.
+ */
+ if (op->value) {
+ op->value = val;
+ return 0;
+ }
+
+ /* Option is boolean, must be either "1", "0", "true" or "false" */
+
+ op_val = strdup(val);
+ if (!op_val)
+ return -1;
+ lower_case(op_val);
+
+ if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
+ op->set = 1;
+ else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0)
+ op->set = 0;
+ free(op_val);
+
+ return 0;
+}
+
/**
* traceevent_plugin_list_options - get list of plugin options
*
@@ -120,6 +167,7 @@ update_option(const char *file, struct pevent_plugin_option *option)
{
struct trace_plugin_options *op;
char *plugin;
+ int ret = 0;
if (option->plugin_alias) {
plugin = strdup(option->plugin_alias);
@@ -144,9 +192,10 @@ update_option(const char *file, struct pevent_plugin_option *option)
if (strcmp(op->option, option->name) != 0)
continue;
- option->value = op->value;
- option->set ^= 1;
- goto out;
+ ret = update_option_value(option, op->value);
+ if (ret)
+ goto out;
+ break;
}
/* first look for unnamed options */
@@ -156,14 +205,13 @@ update_option(const char *file, struct pevent_plugin_option *option)
if (strcmp(op->option, option->name) != 0)
continue;
- option->value = op->value;
- option->set ^= 1;
+ ret = update_option_value(option, op->value);
break;
}
out:
free(plugin);
- return 0;
+ return ret;
}
/**
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c
index dcc6652..3bcada3 100644
--- a/tools/lib/traceevent/kbuffer-parse.c
+++ b/tools/lib/traceevent/kbuffer-parse.c
@@ -372,7 +372,6 @@ translate_data(struct kbuffer *kbuf, void *data, void **rptr,
switch (type_len) {
case KBUFFER_TYPE_PADDING:
*length = read_4(kbuf, data);
- data += *length;
break;
case KBUFFER_TYPE_TIME_EXTEND:
@@ -730,3 +729,14 @@ void kbuffer_set_old_format(struct kbuffer *kbuf)
kbuf->next_event = __old_next_event;
}
+
+/**
+ * kbuffer_start_of_data - return offset of where data starts on subbuffer
+ * @kbuf: The kbuffer
+ *
+ * Returns the location on the subbuffer where the data starts.
+ */
+int kbuffer_start_of_data(struct kbuffer *kbuf)
+{
+ return kbuf->start;
+}
diff --git a/tools/lib/traceevent/kbuffer.h b/tools/lib/traceevent/kbuffer.h
index c831f64..03dce75 100644
--- a/tools/lib/traceevent/kbuffer.h
+++ b/tools/lib/traceevent/kbuffer.h
@@ -63,5 +63,6 @@ int kbuffer_missed_events(struct kbuffer *kbuf);
int kbuffer_subbuffer_size(struct kbuffer *kbuf);
void kbuffer_set_old_format(struct kbuffer *kbuf);
+int kbuffer_start_of_data(struct kbuffer *kbuf);
#endif /* _K_BUFFER_H */
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c
index b502344..0144b3d 100644
--- a/tools/lib/traceevent/parse-filter.c
+++ b/tools/lib/traceevent/parse-filter.c
@@ -1058,6 +1058,7 @@ process_filter(struct event_format *event, struct filter_arg **parg,
*parg = current_op;
else
*parg = current_exp;
+ free(token);
return PEVENT_ERRNO__UNBALANCED_PAREN;
}
break;
@@ -1168,6 +1169,7 @@ process_filter(struct event_format *event, struct filter_arg **parg,
*parg = current_op;
+ free(token);
return 0;
fail_alloc:
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index 5182661..d1deb57 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -31,6 +31,9 @@ OPTIONS
--dump-raw-trace::
Dump raw trace in ASCII.
+--kallsyms=<file>::
+ kallsyms pathname
+
-m::
--modules::
Load module symbols. WARNING: use only with -k and LIVE kernel
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index dd7cccd..4879cf6 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -40,6 +40,11 @@ OPTIONS
Only consider symbols in these comms. CSV that understands
file://filename entries. This option will affect the percentage of
the overhead column. See --percentage for more info.
+--pid=::
+ Only show events for given process ID (comma separated list).
+
+--tid=::
+ Only show events for given thread ID (comma separated list).
-d::
--dsos=::
Only consider symbols in these dsos. CSV that understands
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index a21eec0..7944575 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -193,6 +193,12 @@ OPTIONS
Only display events for these comms. CSV that understands
file://filename entries.
+--pid=::
+ Only show events for given process ID (comma separated list).
+
+--tid=::
+ Only show events for given thread ID (comma separated list).
+
-I::
--show-info::
Display extended information about the perf.data file. This adds
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 473887d..df6307b 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -791,6 +791,8 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
+ OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
+ "file", "kallsyms pathname"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 8c85aeb..64d3623 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -20,6 +20,7 @@
#include <linux/rbtree.h>
#include <linux/string.h>
+#include <locale.h>
struct alloc_stat;
typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
@@ -325,13 +326,13 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
static void print_summary(void)
{
printf("\nSUMMARY\n=======\n");
- printf("Total bytes requested: %lu\n", total_requested);
- printf("Total bytes allocated: %lu\n", total_allocated);
- printf("Total bytes wasted on internal fragmentation: %lu\n",
+ printf("Total bytes requested: %'lu\n", total_requested);
+ printf("Total bytes allocated: %'lu\n", total_allocated);
+ printf("Total bytes wasted on internal fragmentation: %'lu\n",
total_allocated - total_requested);
printf("Internal fragmentation: %f%%\n",
fragmentation(total_requested, total_allocated));
- printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
+ printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
}
static void print_result(struct perf_session *session)
@@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
symbol__init(&session->header.env);
if (!strcmp(argv[0], "stat")) {
+ setlocale(LC_ALL, "");
+
if (cpu__setup_cpunode_map())
goto out_delete;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 0ae4826..b5b2ad4 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -304,7 +304,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
if (rep->mem_mode) {
ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events);
- ret += fprintf(fp, "\n# Sort order : %s", sort_order);
+ ret += fprintf(fp, "\n# Sort order : %s", sort_order ? : default_mem_sort_order);
} else
ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
return ret + fprintf(fp, "\n#\n");
@@ -669,6 +669,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"only consider symbols in these dsos"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"),
+ OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
+ "only consider symbols in these pids"),
+ OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
+ "only consider symbols in these tids"),
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index f2a348b..662366c 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1562,6 +1562,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only display events for these comms"),
+ OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
+ "only consider symbols in these pids"),
+ OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
+ "only consider symbols in these tids"),
OPT_BOOLEAN('I', "show-info", &show_full_info,
"display extended information from perf.data file"),
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 5fb8723..1cb3436 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool,
al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
if (symbol_conf.vmlinux_name) {
- ui__warning("The %s file can't be used.\n%s",
- symbol_conf.vmlinux_name, msg);
+ char serr[256];
+ dso__strerror_load(al.map->dso, serr, sizeof(serr));
+ ui__warning("The %s file can't be used: %s\n%s",
+ symbol_conf.vmlinux_name, serr, msg);
} else {
ui__warning("A vmlinux file was not found.\n%s",
msg);
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 61bf912..b72086e 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1008,6 +1008,32 @@ fallback:
}
filename = symfs_filename;
}
+ } else if (dso__needs_decompress(dso)) {
+ char tmp[PATH_MAX];
+ struct kmod_path m;
+ int fd;
+ bool ret;
+
+ if (kmod_path__parse_ext(&m, symfs_filename))
+ goto out_free_filename;
+
+ snprintf(tmp, PATH_MAX, "/tmp/perf-kmod-XXXXXX");
+
+ fd = mkstemp(tmp);
+ if (fd < 0) {
+ free(m.ext);
+ goto out_free_filename;
+ }
+
+ ret = decompress_to_file(m.ext, symfs_filename, fd);
+
+ free(m.ext);
+ close(fd);
+
+ if (!ret)
+ goto out_free_filename;
+
+ strcpy(symfs_filename, tmp);
}
snprintf(command, sizeof(command),
@@ -1027,7 +1053,7 @@ fallback:
file = popen(command, "r");
if (!file)
- goto out_free_filename;
+ goto out_remove_tmp;
while (!feof(file))
if (symbol__parse_objdump_line(sym, map, file, privsize,
@@ -1042,6 +1068,10 @@ fallback:
delete_last_nop(sym);
pclose(file);
+
+out_remove_tmp:
+ if (dso__needs_decompress(dso))
+ unlink(symfs_filename);
out_free_filename:
if (delete_extract)
kcore_extract__delete(&kce);
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c
index 6da965b..85b5238 100644
--- a/tools/perf/util/cloexec.c
+++ b/tools/perf/util/cloexec.c
@@ -7,6 +7,12 @@
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
+int __weak sched_getcpu(void)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
static int perf_flag_probe(void)
{
/* use 'safest' configuration as used in perf_evsel__fallback() */
diff --git a/tools/perf/util/cloexec.h b/tools/perf/util/cloexec.h
index 94a5a7d..68888c2 100644
--- a/tools/perf/util/cloexec.h
+++ b/tools/perf/util/cloexec.h
@@ -3,4 +3,10 @@
unsigned long perf_event_open_cloexec_flag(void);
+#ifdef __GLIBC_PREREQ
+#if !__GLIBC_PREREQ(2, 6)
+extern int sched_getcpu(void) __THROW;
+#endif
+#endif
+
#endif /* __PERF_CLOEXEC_H */
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 7a7c54b..fc0ddd5 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -165,32 +165,14 @@ bool is_supported_compression(const char *ext)
return false;
}
-bool is_kmodule_extension(const char *ext)
+bool is_kernel_module(const char *pathname)
{
- if (strncmp(ext, "ko", 2))
- return false;
+ struct kmod_path m;
- if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3)))
- return true;
-
- return false;
-}
-
-bool is_kernel_module(const char *pathname, bool *compressed)
-{
- const char *ext = strrchr(pathname, '.');
-
- if (ext == NULL)
- return false;
-
- if (is_supported_compression(ext + 1)) {
- if (compressed)
- *compressed = true;
- ext -= 3;
- } else if (compressed)
- *compressed = false;
+ if (kmod_path__parse(&m, pathname))
+ return NULL;
- return is_kmodule_extension(ext + 1);
+ return m.kmod;
}
bool decompress_to_file(const char *ext, const char *filename, int output_fd)
@@ -1155,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine)
return dso__type_fd(fd);
}
+
+int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)
+{
+ int idx, errnum = dso->load_errno;
+ /*
+ * This must have a same ordering as the enum dso_load_errno.
+ */
+ static const char *dso_load__error_str[] = {
+ "Internal tools/perf/ library error",
+ "Invalid ELF file",
+ "Can not read build id",
+ "Mismatching build id",
+ "Decompression failure",
+ };
+
+ BUG_ON(buflen == 0);
+
+ if (errnum >= 0) {
+ const char *err = strerror_r(errnum, buf, buflen);
+
+ if (err != buf)
+ scnprintf(buf, buflen, "%s", err);
+
+ return 0;
+ }
+
+ if (errnum < __DSO_LOAD_ERRNO__START || errnum >= __DSO_LOAD_ERRNO__END)
+ return -1;
+
+ idx = errnum - __DSO_LOAD_ERRNO__START;
+ scnprintf(buf, buflen, "%s", dso_load__error_str[idx]);
+ return 0;
+}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 3c81d83..e0901b4 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -60,6 +60,31 @@ enum dso_type {
DSO__TYPE_X32BIT,
};
+enum dso_load_errno {
+ DSO_LOAD_ERRNO__SUCCESS = 0,
+
+ /*
+ * Choose an arbitrary negative big number not to clash with standard
+ * errno since SUS requires the errno has distinct positive values.
+ * See 'Issue 6' in the link below.
+ *
+ * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+ */
+ __DSO_LOAD_ERRNO__START = -10000,
+
+ DSO_LOAD_ERRNO__INTERNAL_ERROR = __DSO_LOAD_ERRNO__START,
+
+ /* for symsrc__init() */
+ DSO_LOAD_ERRNO__INVALID_ELF,
+ DSO_LOAD_ERRNO__CANNOT_READ_BUILDID,
+ DSO_LOAD_ERRNO__MISMATCHING_BUILDID,
+
+ /* for decompress_kmodule */
+ DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE,
+
+ __DSO_LOAD_ERRNO__END,
+};
+
#define DSO__SWAP(dso, type, val) \
({ \
type ____r = val; \
@@ -113,6 +138,7 @@ struct dso {
enum dso_swap_type needs_swap;
enum dso_binary_type symtab_type;
enum dso_binary_type binary_type;
+ enum dso_load_errno load_errno;
u8 adjust_symbols:1;
u8 has_build_id:1;
u8 has_srcline:1;
@@ -190,8 +216,7 @@ char dso__symtab_origin(const struct dso *dso);
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
char *root_dir, char *filename, size_t size);
bool is_supported_compression(const char *ext);
-bool is_kmodule_extension(const char *ext);
-bool is_kernel_module(const char *pathname, bool *compressed);
+bool is_kernel_module(const char *pathname);
bool decompress_to_file(const char *ext, const char *filename, int output_fd);
bool dso__needs_decompress(struct dso *dso);
@@ -295,4 +320,6 @@ void dso__free_a2l(struct dso *dso);
enum dso_type dso__type(struct dso *dso, struct machine *machine);
+int dso__strerror_load(struct dso *dso, char *buf, size_t buflen);
+
#endif /* __PERF_DSO */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 1f407f7..fb43215 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1266,7 +1266,7 @@ static int __event_process_build_id(struct build_id_event *bev,
dso__set_build_id(dso, &bev->build_id);
- if (!is_kernel_module(filename, NULL))
+ if (!is_kernel_module(filename))
dso->kernel = dso_type;
build_id__sprintf(dso->build_id, sizeof(dso->build_id),
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index eb95b88..e335330 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -498,6 +498,11 @@ struct map *machine__new_module(struct machine *machine, u64 start,
if (kmod_path__parse_name(&m, filename))
return NULL;
+ map = map_groups__find_by_name(&machine->kmaps, MAP__FUNCTION,
+ m.name);
+ if (map)
+ goto out;
+
dso = machine__module_dso(machine, &m, filename);
if (dso == NULL)
goto out;
@@ -851,6 +856,39 @@ static char *get_kernel_version(const char *root_dir)
return strdup(name);
}
+static bool is_kmod_dso(struct dso *dso)
+{
+ return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE ||
+ dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE;
+}
+
+static int map_groups__set_module_path(struct map_groups *mg, const char *path,
+ struct kmod_path *m)
+{
+ struct map *map;
+ char *long_name;
+
+ map = map_groups__find_by_name(mg, MAP__FUNCTION, m->name);
+ if (map == NULL)
+ return 0;
+
+ long_name = strdup(path);
+ if (long_name == NULL)
+ return -ENOMEM;
+
+ dso__set_long_name(map->dso, long_name, true);
+ dso__kernel_module_get_build_id(map->dso, "");
+
+ /*
+ * Full name could reveal us kmod compression, so
+ * we need to update the symtab_type if needed.
+ */
+ if (m->comp && is_kmod_dso(map->dso))
+ map->dso->symtab_type++;
+
+ return 0;
+}
+
static int map_groups__set_modules_path_dir(struct map_groups *mg,
const char *dir_name, int depth)
{
@@ -889,35 +927,19 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,
if (ret < 0)
goto out;
} else {
- char *dot = strrchr(dent->d_name, '.'),
- dso_name[PATH_MAX];
- struct map *map;
- char *long_name;
-
- if (dot == NULL)
- continue;
+ struct kmod_path m;
- /* On some system, modules are compressed like .ko.gz */
- if (is_supported_compression(dot + 1) &&
- is_kmodule_extension(dot - 2))
- dot -= 3;
+ ret = kmod_path__parse_name(&m, dent->d_name);
+ if (ret)
+ goto out;
- snprintf(dso_name, sizeof(dso_name), "[%.*s]",
- (int)(dot - dent->d_name), dent->d_name);
+ if (m.kmod)
+ ret = map_groups__set_module_path(mg, path, &m);
- strxfrchar(dso_name, '-', '_');
- map = map_groups__find_by_name(mg, MAP__FUNCTION,
- dso_name);
- if (map == NULL)
- continue;
+ free(m.name);
- long_name = strdup(path);
- if (long_name == NULL) {
- ret = -1;
+ if (ret)
goto out;
- }
- dso__set_long_name(map->dso, long_name, true);
- dso__kernel_module_get_build_id(map->dso, "");
}
}
@@ -1087,7 +1109,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
struct dso *dso;
list_for_each_entry(dso, &machine->kernel_dsos.head, node) {
- if (is_kernel_module(dso->long_name, NULL))
+ if (is_kernel_module(dso->long_name))
continue;
kernel = dso;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6b95985..8feac07 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -310,7 +310,10 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
/* Find the address of given function */
map__for_each_symbol_by_name(map, pp->function, sym) {
- address = sym->start;
+ if (uprobes)
+ address = sym->start;
+ else
+ address = map->unmap_ip(map, sym->start);
break;
}
if (!address) {
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 22ebc46..8171fed 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -214,6 +214,11 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->hex.field);
define_event_symbols(event, ev_name, args->hex.size);
break;
+ case PRINT_INT_ARRAY:
+ define_event_symbols(event, ev_name, args->int_array.field);
+ define_event_symbols(event, ev_name, args->int_array.count);
+ define_event_symbols(event, ev_name, args->int_array.el_size);
+ break;
case PRINT_BSTRING:
case PRINT_DYNAMIC_ARRAY:
case PRINT_STRING:
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 0c815a4..2ec5dfb 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -231,6 +231,11 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->hex.field);
define_event_symbols(event, ev_name, args->hex.size);
break;
+ case PRINT_INT_ARRAY:
+ define_event_symbols(event, ev_name, args->int_array.field);
+ define_event_symbols(event, ev_name, args->int_array.count);
+ define_event_symbols(event, ev_name, args->int_array.el_size);
+ break;
case PRINT_STRING:
break;
case PRINT_TYPE:
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 9c01b83..846036a 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -44,6 +44,7 @@ extern struct sort_entry sort_dso_to;
extern struct sort_entry sort_sym_from;
extern struct sort_entry sort_sym_to;
extern enum sort_type sort__first_dimension;
+extern const char default_mem_sort_order[];
struct he_stat {
u64 period;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 62742e4..476268c 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -579,32 +579,37 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata)
static int decompress_kmodule(struct dso *dso, const char *name,
enum dso_binary_type type)
{
- int fd;
- const char *ext = strrchr(name, '.');
+ int fd = -1;
char tmpbuf[] = "/tmp/perf-kmod-XXXXXX";
+ struct kmod_path m;
if (type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP &&
type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP &&
type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
return -1;
- if (!ext || !is_supported_compression(ext + 1)) {
- ext = strrchr(dso->name, '.');
- if (!ext || !is_supported_compression(ext + 1))
- return -1;
- }
+ if (type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+ name = dso->long_name;
- fd = mkstemp(tmpbuf);
- if (fd < 0)
+ if (kmod_path__parse_ext(&m, name) || !m.comp)
return -1;
- if (!decompress_to_file(ext + 1, name, fd)) {
+ fd = mkstemp(tmpbuf);
+ if (fd < 0) {
+ dso->load_errno = errno;
+ goto out;
+ }
+
+ if (!decompress_to_file(m.ext, name, fd)) {
+ dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE;
close(fd);
fd = -1;
}
unlink(tmpbuf);
+out:
+ free(m.ext);
return fd;
}
@@ -633,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
Elf *elf;
int fd;
- if (dso__needs_decompress(dso))
+ if (dso__needs_decompress(dso)) {
fd = decompress_kmodule(dso, name, type);
- else
+ if (fd < 0)
+ return -1;
+ } else {
fd = open(name, O_RDONLY);
-
- if (fd < 0)
- return -1;
+ if (fd < 0) {
+ dso->load_errno = errno;
+ return -1;
+ }
+ }
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL) {
pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
+ dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
goto out_close;
}
if (gelf_getehdr(elf, &ehdr) == NULL) {
+ dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
pr_debug("%s: cannot get elf header.\n", __func__);
goto out_elf_end;
}
- if (dso__swap_init(dso, ehdr.e_ident[EI_DATA]))
+ if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) {
+ dso->load_errno = DSO_LOAD_ERRNO__INTERNAL_ERROR;
goto out_elf_end;
+ }
/* Always reject images with a mismatched build-id: */
if (dso->has_build_id) {
u8 build_id[BUILD_ID_SIZE];
- if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0)
+ if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) {
+ dso->load_errno = DSO_LOAD_ERRNO__CANNOT_READ_BUILDID;
goto out_elf_end;
+ }
- if (!dso__build_id_equal(dso, build_id))
+ if (!dso__build_id_equal(dso, build_id)) {
+ dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID;
goto out_elf_end;
+ }
}
ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
@@ -699,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
}
ss->name = strdup(name);
- if (!ss->name)
+ if (!ss->name) {
+ dso->load_errno = errno;
goto out_elf_end;
+ }
ss->elf = elf;
ss->fd = fd;
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index d7efb03..fd8477c 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -246,13 +246,12 @@ out:
return ret;
}
-int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
- const char *name,
+int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
enum dso_binary_type type)
{
int fd = open(name, O_RDONLY);
if (fd < 0)
- return -1;
+ goto out_errno;
ss->name = strdup(name);
if (!ss->name)
@@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
return 0;
out_close:
close(fd);
+out_errno:
+ dso->load_errno = errno;
return -1;
}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index a690668..fddeb90 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -15,6 +15,7 @@
#include "machine.h"
#include "symbol.h"
#include "strlist.h"
+#include "intlist.h"
#include "header.h"
#include <elf.h>
@@ -1859,6 +1860,20 @@ int setup_list(struct strlist **list, const char *list_str,
return 0;
}
+int setup_intlist(struct intlist **list, const char *list_str,
+ const char *list_name)
+{
+ if (list_str == NULL)
+ return 0;
+
+ *list = intlist__new(list_str);
+ if (!*list) {
+ pr_err("problems parsing %s list\n", list_name);
+ return -1;
+ }
+ return 0;
+}
+
static bool symbol__read_kptr_restrict(void)
{
bool value = false;
@@ -1909,9 +1924,17 @@ int symbol__init(struct perf_session_env *env)
symbol_conf.comm_list_str, "comm") < 0)
goto out_free_dso_list;
+ if (setup_intlist(&symbol_conf.pid_list,
+ symbol_conf.pid_list_str, "pid") < 0)
+ goto out_free_comm_list;
+
+ if (setup_intlist(&symbol_conf.tid_list,
+ symbol_conf.tid_list_str, "tid") < 0)
+ goto out_free_pid_list;
+
if (setup_list(&symbol_conf.sym_list,
symbol_conf.sym_list_str, "symbol") < 0)
- goto out_free_comm_list;
+ goto out_free_tid_list;
/*
* A path to symbols of "/" is identical to ""
@@ -1930,6 +1953,10 @@ int symbol__init(struct perf_session_env *env)
symbol_conf.initialized = true;
return 0;
+out_free_tid_list:
+ intlist__delete(symbol_conf.tid_list);
+out_free_pid_list:
+ intlist__delete(symbol_conf.pid_list);
out_free_comm_list:
strlist__delete(symbol_conf.comm_list);
out_free_dso_list:
@@ -1944,6 +1971,8 @@ void symbol__exit(void)
strlist__delete(symbol_conf.sym_list);
strlist__delete(symbol_conf.dso_list);
strlist__delete(symbol_conf.comm_list);
+ intlist__delete(symbol_conf.tid_list);
+ intlist__delete(symbol_conf.pid_list);
vmlinux_path__exit();
symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
symbol_conf.initialized = false;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index efdaaa5..0956150 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -78,6 +78,7 @@ static inline size_t symbol__size(const struct symbol *sym)
}
struct strlist;
+struct intlist;
struct symbol_conf {
unsigned short priv_size;
@@ -115,6 +116,8 @@ struct symbol_conf {
const char *guestmount;
const char *dso_list_str,
*comm_list_str,
+ *pid_list_str,
+ *tid_list_str,
*sym_list_str,
*col_width_list_str;
struct strlist *dso_list,
@@ -124,6 +127,8 @@ struct symbol_conf {
*dso_to_list,
*sym_from_list,
*sym_to_list;
+ struct intlist *pid_list,
+ *tid_list;
const char *symfs;
};
@@ -295,5 +300,7 @@ int compare_proc_modules(const char *from, const char *to);
int setup_list(struct strlist **list, const char *list_str,
const char *list_name);
+int setup_intlist(struct intlist **list, const char *list_str,
+ const char *list_name);
#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c
index e74c596..a53603b 100644
--- a/tools/perf/util/target.c
+++ b/tools/perf/util/target.c
@@ -123,11 +123,8 @@ int target__strerror(struct target *target, int errnum,
if (errnum >= 0) {
const char *err = strerror_r(errnum, buf, buflen);
- if (err != buf) {
- size_t len = strlen(err);
- memcpy(buf, err, min(buflen - 1, len));
- *(buf + min(buflen - 1, len)) = '\0';
- }
+ if (err != buf)
+ scnprintf(buf, buflen, "%s", err);
return 0;
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 783b668..9b8a54d 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -7,6 +7,7 @@
#include <sys/types.h>
#include "symbol.h"
#include <strlist.h>
+#include <intlist.h>
struct thread_stack;
@@ -100,6 +101,16 @@ static inline bool thread__is_filtered(struct thread *thread)
return true;
}
+ if (symbol_conf.pid_list &&
+ !intlist__has_entry(symbol_conf.pid_list, thread->pid_)) {
+ return true;
+ }
+
+ if (symbol_conf.tid_list &&
+ !intlist__has_entry(symbol_conf.tid_list, thread->tid)) {
+ return true;
+ }
+
return false;
}