Commit baa5a7bc authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 963a70b8 e03eaa40
...@@ -304,7 +304,10 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) ...@@ -304,7 +304,10 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
if (!item) if (!item)
return -1; return -1;
item->comm = strdup(comm); if (comm)
item->comm = strdup(comm);
else
item->comm = strdup("<...>");
if (!item->comm) { if (!item->comm) {
free(item); free(item);
return -1; return -1;
...@@ -318,9 +321,14 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) ...@@ -318,9 +321,14 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
return 0; 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 { struct func_map {
...@@ -758,6 +766,11 @@ static void free_arg(struct print_arg *arg) ...@@ -758,6 +766,11 @@ static void free_arg(struct print_arg *arg)
free_arg(arg->hex.field); free_arg(arg->hex.field);
free_arg(arg->hex.size); free_arg(arg->hex.size);
break; 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: case PRINT_TYPE:
free(arg->typecast.type); free(arg->typecast.type);
free_arg(arg->typecast.item); free_arg(arg->typecast.item);
...@@ -2014,6 +2027,38 @@ process_entry(struct event_format *event __maybe_unused, struct print_arg *arg, ...@@ -2014,6 +2027,38 @@ process_entry(struct event_format *event __maybe_unused, struct print_arg *arg,
return EVENT_ERROR; 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 char *arg_eval (struct print_arg *arg);
static unsigned long long static unsigned long long
...@@ -2486,49 +2531,46 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok) ...@@ -2486,49 +2531,46 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
static enum event_type static enum event_type
process_hex(struct event_format *event, struct print_arg *arg, char **tok) 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)); memset(arg, 0, sizeof(*arg));
arg->type = PRINT_HEX; arg->type = PRINT_HEX;
field = alloc_arg(); if (alloc_and_process_delim(event, ",", &arg->hex.field))
if (!field) { goto out;
do_warning_event(event, "%s: not enough memory!", __func__);
goto out_free;
}
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ",")) if (alloc_and_process_delim(event, ")", &arg->hex.size))
goto out_free; 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(); static enum event_type
if (!field) { process_int_array(struct event_format *event, struct print_arg *arg, char **tok)
do_warning_event(event, "%s: not enough memory!", __func__); {
*tok = NULL; memset(arg, 0, sizeof(*arg));
return EVENT_ERROR; 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, ")")) if (alloc_and_process_delim(event, ",", &arg->int_array.count))
goto out_free; goto free_field;
arg->hex.size = field; if (alloc_and_process_delim(event, ")", &arg->int_array.el_size))
goto free_size;
free_token(token); return read_token_item(tok);
type = read_token_item(tok);
return type;
out_free: free_size:
free_arg(field); free_arg(arg->int_array.count);
free_token(token); free_field:
free_arg(arg->int_array.field);
out:
*tok = NULL; *tok = NULL;
return EVENT_ERROR; return EVENT_ERROR;
} }
...@@ -2828,6 +2870,10 @@ process_function(struct event_format *event, struct print_arg *arg, ...@@ -2828,6 +2870,10 @@ process_function(struct event_format *event, struct print_arg *arg,
free_token(token); free_token(token);
return process_hex(event, arg, tok); 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) { if (strcmp(token, "__get_str") == 0) {
free_token(token); free_token(token);
return process_str(event, arg, tok); return process_str(event, arg, tok);
...@@ -3356,6 +3402,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg ...@@ -3356,6 +3402,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
break; break;
case PRINT_FLAGS: case PRINT_FLAGS:
case PRINT_SYMBOL: case PRINT_SYMBOL:
case PRINT_INT_ARRAY:
case PRINT_HEX: case PRINT_HEX:
break; break;
case PRINT_TYPE: case PRINT_TYPE:
...@@ -3766,6 +3813,54 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, ...@@ -3766,6 +3813,54 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
} }
break; 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: case PRINT_TYPE:
break; break;
case PRINT_STRING: { case PRINT_STRING: {
...@@ -3997,6 +4092,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc ...@@ -3997,6 +4092,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
goto process_again; goto process_again;
case '.': case '.':
goto process_again; goto process_again;
case 'z':
case 'Z':
ls = 1;
goto process_again;
case 'p': case 'p':
ls = 1; ls = 1;
/* fall through */ /* fall through */
...@@ -4939,6 +5038,96 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) ...@@ -4939,6 +5038,96 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid)
return comm; 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 * pevent_data_comm_from_pid - parse the data into the print format
* @s: the trace_seq to write to * @s: the trace_seq to write to
...@@ -5256,6 +5445,15 @@ static void print_args(struct print_arg *args) ...@@ -5256,6 +5445,15 @@ static void print_args(struct print_arg *args)
print_args(args->hex.size); print_args(args->hex.size);
printf(")"); printf(")");
break; 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_STRING:
case PRINT_BSTRING: case PRINT_BSTRING:
printf("__get_str(%s)", args->string.string); printf("__get_str(%s)", args->string.string);
...@@ -6346,6 +6544,7 @@ void pevent_free(struct pevent *pevent) ...@@ -6346,6 +6544,7 @@ void pevent_free(struct pevent *pevent)
free_handler(handle); free_handler(handle);
} }
free(pevent->trace_clock);
free(pevent->events); free(pevent->events);
free(pevent->sort_events); free(pevent->sort_events);
......
...@@ -116,7 +116,7 @@ struct pevent_plugin_option { ...@@ -116,7 +116,7 @@ struct pevent_plugin_option {
char *name; char *name;
char *plugin_alias; char *plugin_alias;
char *description; char *description;
char *value; const char *value;
void *priv; void *priv;
int set; int set;
}; };
...@@ -154,6 +154,10 @@ struct pevent_plugin_option { ...@@ -154,6 +154,10 @@ struct pevent_plugin_option {
* .plugin_alias is used to give a shorter name to access * .plugin_alias is used to give a shorter name to access
* the vairable. Useful if a plugin handles more than one event. * 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) * PEVENT_PLUGIN_ALIAS: (optional)
* The name to use for finding options (uses filename if not defined) * The name to use for finding options (uses filename if not defined)
*/ */
...@@ -247,6 +251,12 @@ struct print_arg_hex { ...@@ -247,6 +251,12 @@ struct print_arg_hex {
struct print_arg *size; 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 print_arg_dynarray {
struct format_field *field; struct format_field *field;
struct print_arg *index; struct print_arg *index;
...@@ -275,6 +285,7 @@ enum print_arg_type { ...@@ -275,6 +285,7 @@ enum print_arg_type {
PRINT_FLAGS, PRINT_FLAGS,
PRINT_SYMBOL, PRINT_SYMBOL,
PRINT_HEX, PRINT_HEX,
PRINT_INT_ARRAY,
PRINT_TYPE, PRINT_TYPE,
PRINT_STRING, PRINT_STRING,
PRINT_BSTRING, PRINT_BSTRING,
...@@ -294,6 +305,7 @@ struct print_arg { ...@@ -294,6 +305,7 @@ struct print_arg {
struct print_arg_flags flags; struct print_arg_flags flags;
struct print_arg_symbol symbol; struct print_arg_symbol symbol;
struct print_arg_hex hex; struct print_arg_hex hex;
struct print_arg_int_array int_array;
struct print_arg_func func; struct print_arg_func func;
struct print_arg_string string; struct print_arg_string string;
struct print_arg_bitmask bitmask; struct print_arg_bitmask bitmask;
...@@ -599,7 +611,7 @@ enum trace_flag_type { ...@@ -599,7 +611,7 @@ enum trace_flag_type {
}; };
int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); 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, int pevent_register_function(struct pevent *pevent, char *name,
unsigned long long addr, char *mod); unsigned long long addr, char *mod);
int pevent_register_print_string(struct pevent *pevent, const char *fmt, 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); ...@@ -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); struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); 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, void pevent_event_info(struct trace_seq *s, struct event_format *event,
struct pevent_record *record); struct pevent_record *record);
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
#include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <dlfcn.h> #include <dlfcn.h>
...@@ -49,6 +50,52 @@ struct plugin_list { ...@@ -49,6 +50,52 @@ struct plugin_list {
void *handle; 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 * traceevent_plugin_list_options - get list of plugin options
* *
...@@ -120,6 +167,7 @@ update_option(const char *file, struct pevent_plugin_option *option) ...@@ -120,6 +167,7 @@ update_option(const char *file, struct pevent_plugin_option *option)
{ {
struct trace_plugin_options *op; struct trace_plugin_options *op;
char *plugin; char *plugin;
int ret = 0;
if (option->plugin_alias) { if (option->plugin_alias) {
plugin = strdup(option->plugin_alias); plugin = strdup(option->plugin_alias);
...@@ -144,9 +192,10 @@ update_option(const char *file, struct pevent_plugin_option *option) ...@@ -144,9 +192,10 @@ update_option(const char *file, struct pevent_plugin_option *option)
if (strcmp(op->option, option->name) != 0) if (strcmp(op->option, option->name) != 0)
continue; continue;
option->value = op->value; ret = update_option_value(option, op->value);
option->set ^= 1; if (ret)
goto out; goto out;
break;
} }
/* first look for unnamed options */ /* first look for unnamed options */
...@@ -156,14 +205,13 @@ update_option(const char *file, struct pevent_plugin_option *option) ...@@ -156,14 +205,13 @@ update_option(const char *file, struct pevent_plugin_option *option)
if (strcmp(op->option, option->name) != 0) if (strcmp(op->option, option->name) != 0)
continue; continue;
option->value = op->value; ret = update_option_value(option, op->value);
option->set ^= 1;
break; break;
} }
out: out:
free(plugin); free(plugin);
return 0; return ret;
} }
/** /**
......
...@@ -372,7 +372,6 @@ translate_data(struct kbuffer *kbuf, void *data, void **rptr, ...@@ -372,7 +372,6 @@ translate_data(struct kbuffer *kbuf, void *data, void **rptr,
switch (type_len) { switch (type_len) {
case KBUFFER_TYPE_PADDING: case KBUFFER_TYPE_PADDING:
*length = read_4(kbuf, data); *length = read_4(kbuf, data);
data += *length;
break; break;
case KBUFFER_TYPE_TIME_EXTEND: case KBUFFER_TYPE_TIME_EXTEND:
...@@ -730,3 +729,14 @@ void kbuffer_set_old_format(struct kbuffer *kbuf) ...@@ -730,3 +729,14 @@ void kbuffer_set_old_format(struct kbuffer *kbuf)
kbuf->next_event = __old_next_event; 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;
}
...@@ -63,5 +63,6 @@ int kbuffer_missed_events(struct kbuffer *kbuf); ...@@ -63,5 +63,6 @@ int kbuffer_missed_events(struct kbuffer *kbuf);
int kbuffer_subbuffer_size(struct kbuffer *kbuf); int kbuffer_subbuffer_size(struct kbuffer *kbuf);
void kbuffer_set_old_format(struct kbuffer *kbuf); void kbuffer_set_old_format(struct kbuffer *kbuf);
int kbuffer_start_of_data(struct kbuffer *kbuf);
#endif /* _K_BUFFER_H */ #endif /* _K_BUFFER_H */
...@@ -1058,6 +1058,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, ...@@ -1058,6 +1058,7 @@ process_filter(struct event_format *event, struct filter_arg **parg,
*parg = current_op; *parg = current_op;
else else
*parg = current_exp; *parg = current_exp;
free(token);
return PEVENT_ERRNO__UNBALANCED_PAREN; return PEVENT_ERRNO__UNBALANCED_PAREN;
} }
break; break;
...@@ -1168,6 +1169,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, ...@@ -1168,6 +1169,7 @@ process_filter(struct event_format *event, struct filter_arg **parg,
*parg = current_op; *parg = current_op;
free(token);
return 0; return 0;
fail_alloc: fail_alloc:
......
...@@ -31,6 +31,9 @@ OPTIONS ...@@ -31,6 +31,9 @@ OPTIONS
--dump-raw-trace:: --dump-raw-trace::
Dump raw trace in ASCII. Dump raw trace in ASCII.
--kallsyms=<file>::
kallsyms pathname
-m:: -m::
--modules:: --modules::
Load module symbols. WARNING: use only with -k and LIVE kernel Load module symbols. WARNING: use only with -k and LIVE kernel
......
...@@ -40,6 +40,11 @@ OPTIONS ...@@ -40,6 +40,11 @@ OPTIONS
Only consider symbols in these comms. CSV that understands Only consider symbols in these comms. CSV that understands
file://filename entries. This option will affect the percentage of file://filename entries. This option will affect the percentage of
the overhead column. See --percentage for more info. 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:: -d::
--dsos=:: --dsos=::
Only consider symbols in these dsos. CSV that understands Only consider symbols in these dsos. CSV that understands
......
...@@ -193,6 +193,12 @@ OPTIONS ...@@ -193,6 +193,12 @@ OPTIONS
Only display events for these comms. CSV that understands Only display events for these comms. CSV that understands
file://filename entries. 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:: -I::
--show-info:: --show-info::
Display extended information about the perf.data file. This adds Display extended information about the perf.data file. This adds
......
...@@ -791,6 +791,8 @@ static const struct option options[] = { ...@@ -791,6 +791,8 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"), "dump raw trace in ASCII"),
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 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, OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"), "load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/string.h> #include <linux/string.h>
#include <locale.h>
struct alloc_stat; struct alloc_stat;
typedef int (*sort_fn_t)(struct alloc_stat *, 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, ...@@ -325,13 +326,13 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
static void print_summary(void) static void print_summary(void)
{ {
printf("\nSUMMARY\n=======\n"); printf("\nSUMMARY\n=======\n");
printf("Total bytes requested: %lu\n", total_requested); printf("Total bytes requested: %'lu\n", total_requested);
printf("Total bytes allocated: %lu\n", total_allocated); printf("Total bytes allocated: %'lu\n", total_allocated);
printf("Total bytes wasted on internal fragmentation: %lu\n", printf("Total bytes wasted on internal fragmentation: %'lu\n",
total_allocated - total_requested); total_allocated - total_requested);
printf("Internal fragmentation: %f%%\n", printf("Internal fragmentation: %f%%\n",
fragmentation(total_requested, total_allocated)); 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) 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) ...@@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
symbol__init(&session->header.env); symbol__init(&session->header.env);
if (!strcmp(argv[0], "stat")) { if (!strcmp(argv[0], "stat")) {
setlocale(LC_ALL, "");
if (cpu__setup_cpunode_map()) if (cpu__setup_cpunode_map())
goto out_delete; goto out_delete;
......
...@@ -304,7 +304,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report ...@@ -304,7 +304,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
if (rep->mem_mode) { if (rep->mem_mode) {
ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events); 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 } else
ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
return ret + fprintf(fp, "\n#\n"); return ret + fprintf(fp, "\n#\n");
...@@ -669,6 +669,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -669,6 +669,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"only consider symbols in these dsos"), "only consider symbols in these dsos"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"), "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...]", OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
"only consider these symbols"), "only consider these symbols"),
OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter", OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",
......
...@@ -1562,6 +1562,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -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', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only display events for these comms"), "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, OPT_BOOLEAN('I', "show-info", &show_full_info,
"display extended information from perf.data file"), "display extended information from perf.data file"),
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
......
...@@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool,
al.map == machine->vmlinux_maps[MAP__FUNCTION] && al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
if (symbol_conf.vmlinux_name) { if (symbol_conf.vmlinux_name) {
ui__warning("The %s file can't be used.\n%s", char serr[256];
symbol_conf.vmlinux_name, msg); 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 { } else {
ui__warning("A vmlinux file was not found.\n%s", ui__warning("A vmlinux file was not found.\n%s",
msg); msg);
......
...@@ -1008,6 +1008,32 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) ...@@ -1008,6 +1008,32 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
} }
filename = symfs_filename; 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), snprintf(command, sizeof(command),
...@@ -1027,7 +1053,7 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) ...@@ -1027,7 +1053,7 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
file = popen(command, "r"); file = popen(command, "r");
if (!file) if (!file)
goto out_free_filename; goto out_remove_tmp;
while (!feof(file)) while (!feof(file))
if (symbol__parse_objdump_line(sym, map, file, privsize, if (symbol__parse_objdump_line(sym, map, file, privsize,
...@@ -1042,6 +1068,10 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) ...@@ -1042,6 +1068,10 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
delete_last_nop(sym); delete_last_nop(sym);
pclose(file); pclose(file);
out_remove_tmp:
if (dso__needs_decompress(dso))
unlink(symfs_filename);
out_free_filename: out_free_filename:
if (delete_extract) if (delete_extract)
kcore_extract__delete(&kce); kcore_extract__delete(&kce);
......
...@@ -7,6 +7,12 @@ ...@@ -7,6 +7,12 @@
static unsigned long flag = PERF_FLAG_FD_CLOEXEC; static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
int __weak sched_getcpu(void)
{
errno = ENOSYS;
return -1;
}
static int perf_flag_probe(void) static int perf_flag_probe(void)
{ {
/* use 'safest' configuration as used in perf_evsel__fallback() */ /* use 'safest' configuration as used in perf_evsel__fallback() */
......
...@@ -3,4 +3,10 @@ ...@@ -3,4 +3,10 @@
unsigned long perf_event_open_cloexec_flag(void); 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 */ #endif /* __PERF_CLOEXEC_H */
...@@ -165,32 +165,14 @@ bool is_supported_compression(const char *ext) ...@@ -165,32 +165,14 @@ bool is_supported_compression(const char *ext)
return false; return false;
} }
bool is_kmodule_extension(const char *ext) bool is_kernel_module(const char *pathname)
{ {
if (strncmp(ext, "ko", 2)) struct kmod_path m;
return false;
if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3))) if (kmod_path__parse(&m, pathname))
return true; return NULL;
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;
return is_kmodule_extension(ext + 1); return m.kmod;
} }
bool decompress_to_file(const char *ext, const char *filename, int output_fd) 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) ...@@ -1155,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine)
return dso__type_fd(fd); 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;
}
...@@ -60,6 +60,31 @@ enum dso_type { ...@@ -60,6 +60,31 @@ enum dso_type {
DSO__TYPE_X32BIT, 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) \ #define DSO__SWAP(dso, type, val) \
({ \ ({ \
type ____r = val; \ type ____r = val; \
...@@ -113,6 +138,7 @@ struct dso { ...@@ -113,6 +138,7 @@ struct dso {
enum dso_swap_type needs_swap; enum dso_swap_type needs_swap;
enum dso_binary_type symtab_type; enum dso_binary_type symtab_type;
enum dso_binary_type binary_type; enum dso_binary_type binary_type;
enum dso_load_errno load_errno;
u8 adjust_symbols:1; u8 adjust_symbols:1;
u8 has_build_id:1; u8 has_build_id:1;
u8 has_srcline:1; u8 has_srcline:1;
...@@ -190,8 +216,7 @@ char dso__symtab_origin(const struct dso *dso); ...@@ -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, int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
char *root_dir, char *filename, size_t size); char *root_dir, char *filename, size_t size);
bool is_supported_compression(const char *ext); bool is_supported_compression(const char *ext);
bool is_kmodule_extension(const char *ext); bool is_kernel_module(const char *pathname);
bool is_kernel_module(const char *pathname, bool *compressed);
bool decompress_to_file(const char *ext, const char *filename, int output_fd); bool decompress_to_file(const char *ext, const char *filename, int output_fd);
bool dso__needs_decompress(struct dso *dso); bool dso__needs_decompress(struct dso *dso);
...@@ -295,4 +320,6 @@ void dso__free_a2l(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); 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 */ #endif /* __PERF_DSO */
...@@ -1266,7 +1266,7 @@ static int __event_process_build_id(struct build_id_event *bev, ...@@ -1266,7 +1266,7 @@ static int __event_process_build_id(struct build_id_event *bev,
dso__set_build_id(dso, &bev->build_id); dso__set_build_id(dso, &bev->build_id);
if (!is_kernel_module(filename, NULL)) if (!is_kernel_module(filename))
dso->kernel = dso_type; dso->kernel = dso_type;
build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id__sprintf(dso->build_id, sizeof(dso->build_id),
......
...@@ -498,6 +498,11 @@ struct map *machine__new_module(struct machine *machine, u64 start, ...@@ -498,6 +498,11 @@ struct map *machine__new_module(struct machine *machine, u64 start,
if (kmod_path__parse_name(&m, filename)) if (kmod_path__parse_name(&m, filename))
return NULL; 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); dso = machine__module_dso(machine, &m, filename);
if (dso == NULL) if (dso == NULL)
goto out; goto out;
...@@ -851,6 +856,39 @@ static char *get_kernel_version(const char *root_dir) ...@@ -851,6 +856,39 @@ static char *get_kernel_version(const char *root_dir)
return strdup(name); 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, static int map_groups__set_modules_path_dir(struct map_groups *mg,
const char *dir_name, int depth) const char *dir_name, int depth)
{ {
...@@ -889,35 +927,19 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, ...@@ -889,35 +927,19 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,
if (ret < 0) if (ret < 0)
goto out; goto out;
} else { } else {
char *dot = strrchr(dent->d_name, '.'), struct kmod_path m;
dso_name[PATH_MAX];
struct map *map;
char *long_name;
if (dot == NULL)
continue;
/* On some system, modules are compressed like .ko.gz */ ret = kmod_path__parse_name(&m, dent->d_name);
if (is_supported_compression(dot + 1) && if (ret)
is_kmodule_extension(dot - 2)) goto out;
dot -= 3;
snprintf(dso_name, sizeof(dso_name), "[%.*s]", if (m.kmod)
(int)(dot - dent->d_name), dent->d_name); ret = map_groups__set_module_path(mg, path, &m);
strxfrchar(dso_name, '-', '_'); free(m.name);
map = map_groups__find_by_name(mg, MAP__FUNCTION,
dso_name);
if (map == NULL)
continue;
long_name = strdup(path); if (ret)
if (long_name == NULL) {
ret = -1;
goto out; 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, ...@@ -1087,7 +1109,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
struct dso *dso; struct dso *dso;
list_for_each_entry(dso, &machine->kernel_dsos.head, node) { 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; continue;
kernel = dso; kernel = dso;
......
...@@ -310,7 +310,10 @@ static int find_alternative_probe_point(struct debuginfo *dinfo, ...@@ -310,7 +310,10 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
/* Find the address of given function */ /* Find the address of given function */
map__for_each_symbol_by_name(map, pp->function, sym) { 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; break;
} }
if (!address) { if (!address) {
......
...@@ -214,6 +214,11 @@ static void define_event_symbols(struct event_format *event, ...@@ -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.field);
define_event_symbols(event, ev_name, args->hex.size); define_event_symbols(event, ev_name, args->hex.size);
break; 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_BSTRING:
case PRINT_DYNAMIC_ARRAY: case PRINT_DYNAMIC_ARRAY:
case PRINT_STRING: case PRINT_STRING:
......
...@@ -231,6 +231,11 @@ static void define_event_symbols(struct event_format *event, ...@@ -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.field);
define_event_symbols(event, ev_name, args->hex.size); define_event_symbols(event, ev_name, args->hex.size);
break; 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: case PRINT_STRING:
break; break;
case PRINT_TYPE: case PRINT_TYPE:
......
...@@ -44,6 +44,7 @@ extern struct sort_entry sort_dso_to; ...@@ -44,6 +44,7 @@ extern struct sort_entry sort_dso_to;
extern struct sort_entry sort_sym_from; extern struct sort_entry sort_sym_from;
extern struct sort_entry sort_sym_to; extern struct sort_entry sort_sym_to;
extern enum sort_type sort__first_dimension; extern enum sort_type sort__first_dimension;
extern const char default_mem_sort_order[];
struct he_stat { struct he_stat {
u64 period; u64 period;
......
...@@ -579,32 +579,37 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata) ...@@ -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, static int decompress_kmodule(struct dso *dso, const char *name,
enum dso_binary_type type) enum dso_binary_type type)
{ {
int fd; int fd = -1;
const char *ext = strrchr(name, '.');
char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; char tmpbuf[] = "/tmp/perf-kmod-XXXXXX";
struct kmod_path m;
if (type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && if (type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP &&
type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP && type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP &&
type != DSO_BINARY_TYPE__BUILD_ID_CACHE) type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
return -1; return -1;
if (!ext || !is_supported_compression(ext + 1)) { if (type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
ext = strrchr(dso->name, '.'); name = dso->long_name;
if (!ext || !is_supported_compression(ext + 1))
return -1;
}
fd = mkstemp(tmpbuf); if (kmod_path__parse_ext(&m, name) || !m.comp)
if (fd < 0)
return -1; 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); close(fd);
fd = -1; fd = -1;
} }
unlink(tmpbuf); unlink(tmpbuf);
out:
free(m.ext);
return fd; return fd;
} }
...@@ -633,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, ...@@ -633,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
Elf *elf; Elf *elf;
int fd; int fd;
if (dso__needs_decompress(dso)) if (dso__needs_decompress(dso)) {
fd = decompress_kmodule(dso, name, type); fd = decompress_kmodule(dso, name, type);
else if (fd < 0)
return -1;
} else {
fd = open(name, O_RDONLY); fd = open(name, O_RDONLY);
if (fd < 0) {
if (fd < 0) dso->load_errno = errno;
return -1; return -1;
}
}
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL) { if (elf == NULL) {
pr_debug("%s: cannot read %s ELF file.\n", __func__, name); pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
goto out_close; goto out_close;
} }
if (gelf_getehdr(elf, &ehdr) == NULL) { if (gelf_getehdr(elf, &ehdr) == NULL) {
dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
pr_debug("%s: cannot get elf header.\n", __func__); pr_debug("%s: cannot get elf header.\n", __func__);
goto out_elf_end; 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; goto out_elf_end;
}
/* Always reject images with a mismatched build-id: */ /* Always reject images with a mismatched build-id: */
if (dso->has_build_id) { if (dso->has_build_id) {
u8 build_id[BUILD_ID_SIZE]; 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; 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; goto out_elf_end;
}
} }
ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
...@@ -699,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, ...@@ -699,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
} }
ss->name = strdup(name); ss->name = strdup(name);
if (!ss->name) if (!ss->name) {
dso->load_errno = errno;
goto out_elf_end; goto out_elf_end;
}
ss->elf = elf; ss->elf = elf;
ss->fd = fd; ss->fd = fd;
......
...@@ -246,13 +246,12 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) ...@@ -246,13 +246,12 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
return ret; return ret;
} }
int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
const char *name,
enum dso_binary_type type) enum dso_binary_type type)
{ {
int fd = open(name, O_RDONLY); int fd = open(name, O_RDONLY);
if (fd < 0) if (fd < 0)
return -1; goto out_errno;
ss->name = strdup(name); ss->name = strdup(name);
if (!ss->name) if (!ss->name)
...@@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, ...@@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
return 0; return 0;
out_close: out_close:
close(fd); close(fd);
out_errno:
dso->load_errno = errno;
return -1; return -1;
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "machine.h" #include "machine.h"
#include "symbol.h" #include "symbol.h"
#include "strlist.h" #include "strlist.h"
#include "intlist.h"
#include "header.h" #include "header.h"
#include <elf.h> #include <elf.h>
...@@ -1859,6 +1860,20 @@ int setup_list(struct strlist **list, const char *list_str, ...@@ -1859,6 +1860,20 @@ int setup_list(struct strlist **list, const char *list_str,
return 0; 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) static bool symbol__read_kptr_restrict(void)
{ {
bool value = false; bool value = false;
...@@ -1909,9 +1924,17 @@ int symbol__init(struct perf_session_env *env) ...@@ -1909,9 +1924,17 @@ int symbol__init(struct perf_session_env *env)
symbol_conf.comm_list_str, "comm") < 0) symbol_conf.comm_list_str, "comm") < 0)
goto out_free_dso_list; 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, if (setup_list(&symbol_conf.sym_list,
symbol_conf.sym_list_str, "symbol") < 0) 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 "" * A path to symbols of "/" is identical to ""
...@@ -1930,6 +1953,10 @@ int symbol__init(struct perf_session_env *env) ...@@ -1930,6 +1953,10 @@ int symbol__init(struct perf_session_env *env)
symbol_conf.initialized = true; symbol_conf.initialized = true;
return 0; 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: out_free_comm_list:
strlist__delete(symbol_conf.comm_list); strlist__delete(symbol_conf.comm_list);
out_free_dso_list: out_free_dso_list:
...@@ -1944,6 +1971,8 @@ void symbol__exit(void) ...@@ -1944,6 +1971,8 @@ void symbol__exit(void)
strlist__delete(symbol_conf.sym_list); strlist__delete(symbol_conf.sym_list);
strlist__delete(symbol_conf.dso_list); strlist__delete(symbol_conf.dso_list);
strlist__delete(symbol_conf.comm_list); strlist__delete(symbol_conf.comm_list);
intlist__delete(symbol_conf.tid_list);
intlist__delete(symbol_conf.pid_list);
vmlinux_path__exit(); vmlinux_path__exit();
symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
symbol_conf.initialized = false; symbol_conf.initialized = false;
......
...@@ -78,6 +78,7 @@ static inline size_t symbol__size(const struct symbol *sym) ...@@ -78,6 +78,7 @@ static inline size_t symbol__size(const struct symbol *sym)
} }
struct strlist; struct strlist;
struct intlist;
struct symbol_conf { struct symbol_conf {
unsigned short priv_size; unsigned short priv_size;
...@@ -115,6 +116,8 @@ struct symbol_conf { ...@@ -115,6 +116,8 @@ struct symbol_conf {
const char *guestmount; const char *guestmount;
const char *dso_list_str, const char *dso_list_str,
*comm_list_str, *comm_list_str,
*pid_list_str,
*tid_list_str,
*sym_list_str, *sym_list_str,
*col_width_list_str; *col_width_list_str;
struct strlist *dso_list, struct strlist *dso_list,
...@@ -124,6 +127,8 @@ struct symbol_conf { ...@@ -124,6 +127,8 @@ struct symbol_conf {
*dso_to_list, *dso_to_list,
*sym_from_list, *sym_from_list,
*sym_to_list; *sym_to_list;
struct intlist *pid_list,
*tid_list;
const char *symfs; const char *symfs;
}; };
...@@ -295,5 +300,7 @@ int compare_proc_modules(const char *from, const char *to); ...@@ -295,5 +300,7 @@ int compare_proc_modules(const char *from, const char *to);
int setup_list(struct strlist **list, const char *list_str, int setup_list(struct strlist **list, const char *list_str,
const char *list_name); const char *list_name);
int setup_intlist(struct intlist **list, const char *list_str,
const char *list_name);
#endif /* __PERF_SYMBOL */ #endif /* __PERF_SYMBOL */
...@@ -123,11 +123,8 @@ int target__strerror(struct target *target, int errnum, ...@@ -123,11 +123,8 @@ int target__strerror(struct target *target, int errnum,
if (errnum >= 0) { if (errnum >= 0) {
const char *err = strerror_r(errnum, buf, buflen); const char *err = strerror_r(errnum, buf, buflen);
if (err != buf) { if (err != buf)
size_t len = strlen(err); scnprintf(buf, buflen, "%s", err);
memcpy(buf, err, min(buflen - 1, len));
*(buf + min(buflen - 1, len)) = '\0';
}
return 0; return 0;
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <sys/types.h> #include <sys/types.h>
#include "symbol.h" #include "symbol.h"
#include <strlist.h> #include <strlist.h>
#include <intlist.h>
struct thread_stack; struct thread_stack;
...@@ -100,6 +101,16 @@ static inline bool thread__is_filtered(struct thread *thread) ...@@ -100,6 +101,16 @@ static inline bool thread__is_filtered(struct thread *thread)
return true; 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; return false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment