Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit baa5a7bc authored by Ingo Molnar's avatar Ingo Molnar
Browse files

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
Loading
Loading
Loading
Loading
+233 −34
Original line number Original line Diff line number Diff line
@@ -304,7 +304,10 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
	if (!item)
	if (!item)
		return -1;
		return -1;


	if (comm)
		item->comm = strdup(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)
	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)
		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,
	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)
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, ","))
		goto out_free;


	arg->hex.field = field;
	if (alloc_and_process_delim(event, ")", &arg->hex.size))
		goto free_field;


	free_token(token);
	return read_token_item(tok);


	field = alloc_arg();
free_field:
	if (!field) {
	free_arg(arg->hex.field);
		do_warning_event(event, "%s: not enough memory!", __func__);
out:
	*tok = NULL;
	*tok = NULL;
	return EVENT_ERROR;
	return EVENT_ERROR;
}
}


	type = process_arg(event, field, &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;


	if (test_type_token(type, token, EVENT_DELIM, ")"))
	if (alloc_and_process_delim(event, ",", &arg->int_array.field))
		goto out_free;
		goto out;


	arg->hex.size = field;
	if (alloc_and_process_delim(event, ",", &arg->int_array.count))
		goto free_field;


	free_token(token);
	if (alloc_and_process_delim(event, ")", &arg->int_array.el_size))
	type = read_token_item(tok);
		goto free_size;
	return type;


 out_free:
	return read_token_item(tok);
	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;
	*tok = NULL;
	return EVENT_ERROR;
	return EVENT_ERROR;
}
}
@@ -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
		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,
		}
		}
		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
				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)
	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)
		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)
		free_handler(handle);
		free_handler(handle);
	}
	}


	free(pevent->trace_clock);
	free(pevent->events);
	free(pevent->events);
	free(pevent->sort_events);
	free(pevent->sort_events);


+19 −2
Original line number Original line Diff line number Diff line
@@ -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 {
 *   .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 {
	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 {
	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 {
		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 {
};
};


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);
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,
+54 −6
Original line number Original line Diff line number Diff line
@@ -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 {
	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)
{
{
	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)
		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)
		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;
}
}


/**
/**
+11 −1
Original line number Original line Diff line number Diff line
@@ -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)


	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;
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -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 */
Loading