perf/core improvements and fixes:
User visible: - 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: - 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> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJVEY02AAoJEBpxZoYYoA71LpYH/i8UiaLXFtOZiaPz880MkhKy G7FVvsJ7mNn89/m3NyO65ueH9PwJU9SMHPEIwhVHQKPW/1l6Sp/t1IzvSvg9qua0 SW4L41ZdFDcCdLWhHpm/RhE8Pd8n54cmqiS6hBUCRR58moOHB1J1kSJQzyeYABvz TvlodCLvzddsX5R8ErEpPO1pZFRljX/xOuePJP8A5dz8ceih1MyoUv7juTwHJiGY Ra4aGw+EpozipAzrA1h2pFsGltUKFHNnxKnpvQKibk/2HQ6RnR3TUagRMIb4vL1Y RzdZATz77GTCLGX5BDe1LgK0Ogsnyev+bSx4Cfcl4d0F5IXI348+IMZb96GAGQE= =WelS -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
baa5a7bc5d
|
@ -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;
|
||||
}
|
||||
if (alloc_and_process_delim(event, ",", &arg->hex.field))
|
||||
goto out;
|
||||
|
||||
type = process_arg(event, field, &token);
|
||||
if (alloc_and_process_delim(event, ")", &arg->hex.size))
|
||||
goto free_field;
|
||||
|
||||
if (test_type_token(type, token, EVENT_DELIM, ","))
|
||||
goto out_free;
|
||||
return read_token_item(tok);
|
||||
|
||||
arg->hex.field = field;
|
||||
free_field:
|
||||
free_arg(arg->hex.field);
|
||||
out:
|
||||
*tok = NULL;
|
||||
return EVENT_ERROR;
|
||||
}
|
||||
|
||||
free_token(token);
|
||||
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;
|
||||
|
||||
field = alloc_arg();
|
||||
if (!field) {
|
||||
do_warning_event(event, "%s: not enough memory!", __func__);
|
||||
*tok = NULL;
|
||||
return EVENT_ERROR;
|
||||
}
|
||||
if (alloc_and_process_delim(event, ",", &arg->int_array.field))
|
||||
goto out;
|
||||
|
||||
type = process_arg(event, field, &token);
|
||||
if (alloc_and_process_delim(event, ",", &arg->int_array.count))
|
||||
goto free_field;
|
||||
|
||||
if (test_type_token(type, token, EVENT_DELIM, ")"))
|
||||
goto out_free;
|
||||
if (alloc_and_process_delim(event, ")", &arg->int_array.el_size))
|
||||
goto free_size;
|
||||
|
||||
arg->hex.size = field;
|
||||
return read_token_item(tok);
|
||||
|
||||
free_token(token);
|
||||
type = read_token_item(tok);
|
||||
return type;
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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...]",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
if (kmod_path__parse(&m, pathname))
|
||||
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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
struct kmod_path m;
|
||||
|
||||
if (dot == NULL)
|
||||
continue;
|
||||
|
||||
/* On some system, modules are compressed like .ko.gz */
|
||||
if (is_supported_compression(dot + 1) &&
|
||||
is_kmodule_extension(dot - 2))
|
||||
dot -= 3;
|
||||
|
||||
snprintf(dso_name, sizeof(dso_name), "[%.*s]",
|
||||
(int)(dot - dent->d_name), dent->d_name);
|
||||
|
||||
strxfrchar(dso_name, '-', '_');
|
||||
map = map_groups__find_by_name(mg, MAP__FUNCTION,
|
||||
dso_name);
|
||||
if (map == NULL)
|
||||
continue;
|
||||
|
||||
long_name = strdup(path);
|
||||
if (long_name == NULL) {
|
||||
ret = -1;
|
||||
ret = kmod_path__parse_name(&m, dent->d_name);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (m.kmod)
|
||||
ret = map_groups__set_module_path(mg, path, &m);
|
||||
|
||||
free(m.name);
|
||||
|
||||
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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue