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

Commit 6b4827ad authored by Tom Zanussi's avatar Tom Zanussi Committed by Steven Rostedt
Browse files

tracing: Add hist trigger 'execname' modifier

Allow users to have common_pid field values displayed as program names
in the output by appending '.execname' to a common_pid field name:

   # echo hist:keys=common_pid.execname ... \
              [ if filter] > event/trigger

Link: http://lkml.kernel.org/r/e172e81f10f5b8d1f08450e3763c850f39fbf698.1457029949.git.tom.zanussi@linux.intel.com



Signed-off-by: default avatarTom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Reviewed-by: default avatarNamhyung Kim <namhyung@kernel.org>
Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent c6afad49
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -3857,7 +3857,8 @@ static const char readme_msg[] =
	"\t    following modifiers to the field name, as applicable:\n\n"
	"\t    following modifiers to the field name, as applicable:\n\n"
	"\t            .hex        display a number as a hex value\n"
	"\t            .hex        display a number as a hex value\n"
	"\t            .sym        display an address as a symbol\n"
	"\t            .sym        display an address as a symbol\n"
	"\t            .sym-offset display an address as a symbol and offset\n\n"
	"\t            .sym-offset display an address as a symbol and offset\n"
	"\t            .execname   display a common_pid as a program name\n\n"
	"\t    The 'pause' parameter can be used to pause an existing hist\n"
	"\t    The 'pause' parameter can be used to pause an existing hist\n"
	"\t    trigger or to start a hist trigger but not log any events\n"
	"\t    trigger or to start a hist trigger but not log any events\n"
	"\t    until told to do so.  'continue' can be used to start or\n"
	"\t    until told to do so.  'continue' can be used to start or\n"
+99 −1
Original line number Original line Diff line number Diff line
@@ -83,6 +83,7 @@ enum hist_field_flags {
	HIST_FIELD_FL_HEX		= 8,
	HIST_FIELD_FL_HEX		= 8,
	HIST_FIELD_FL_SYM		= 16,
	HIST_FIELD_FL_SYM		= 16,
	HIST_FIELD_FL_SYM_OFFSET	= 32,
	HIST_FIELD_FL_SYM_OFFSET	= 32,
	HIST_FIELD_FL_EXECNAME		= 64,
};
};


struct hist_trigger_attrs {
struct hist_trigger_attrs {
@@ -232,6 +233,73 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
	return ERR_PTR(ret);
	return ERR_PTR(ret);
}
}


static inline void save_comm(char *comm, struct task_struct *task)
{
	if (!task->pid) {
		strcpy(comm, "<idle>");
		return;
	}

	if (WARN_ON_ONCE(task->pid < 0)) {
		strcpy(comm, "<XXX>");
		return;
	}

	memcpy(comm, task->comm, TASK_COMM_LEN);
}

static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt)
{
	kfree((char *)elt->private_data);
}

static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt)
{
	struct hist_trigger_data *hist_data = elt->map->private_data;
	struct hist_field *key_field;
	unsigned int i;

	for_each_hist_key_field(i, hist_data) {
		key_field = hist_data->fields[i];

		if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
			unsigned int size = TASK_COMM_LEN + 1;

			elt->private_data = kzalloc(size, GFP_KERNEL);
			if (!elt->private_data)
				return -ENOMEM;
			break;
		}
	}

	return 0;
}

static void hist_trigger_elt_comm_copy(struct tracing_map_elt *to,
				       struct tracing_map_elt *from)
{
	char *comm_from = from->private_data;
	char *comm_to = to->private_data;

	if (comm_from)
		memcpy(comm_to, comm_from, TASK_COMM_LEN + 1);
}

static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt)
{
	char *comm = elt->private_data;

	if (comm)
		save_comm(comm, current);
}

static const struct tracing_map_ops hist_trigger_elt_comm_ops = {
	.elt_alloc	= hist_trigger_elt_comm_alloc,
	.elt_copy	= hist_trigger_elt_comm_copy,
	.elt_free	= hist_trigger_elt_comm_free,
	.elt_init	= hist_trigger_elt_comm_init,
};

static void destroy_hist_field(struct hist_field *hist_field)
static void destroy_hist_field(struct hist_field *hist_field)
{
{
	kfree(hist_field);
	kfree(hist_field);
@@ -403,6 +471,9 @@ static int create_key_field(struct hist_trigger_data *hist_data,
			flags |= HIST_FIELD_FL_SYM;
			flags |= HIST_FIELD_FL_SYM;
		else if (strcmp(field_str, "sym-offset") == 0)
		else if (strcmp(field_str, "sym-offset") == 0)
			flags |= HIST_FIELD_FL_SYM_OFFSET;
			flags |= HIST_FIELD_FL_SYM_OFFSET;
		else if ((strcmp(field_str, "execname") == 0) &&
			 (strcmp(field_name, "common_pid") == 0))
			flags |= HIST_FIELD_FL_EXECNAME;
		else {
		else {
			ret = -EINVAL;
			ret = -EINVAL;
			goto out;
			goto out;
@@ -624,11 +695,27 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
	return 0;
	return 0;
}
}


static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
{
	struct hist_field *key_field;
	unsigned int i;

	for_each_hist_key_field(i, hist_data) {
		key_field = hist_data->fields[i];

		if (key_field->flags & HIST_FIELD_FL_EXECNAME)
			return true;
	}

	return false;
}

static struct hist_trigger_data *
static struct hist_trigger_data *
create_hist_data(unsigned int map_bits,
create_hist_data(unsigned int map_bits,
		 struct hist_trigger_attrs *attrs,
		 struct hist_trigger_attrs *attrs,
		 struct trace_event_file *file)
		 struct trace_event_file *file)
{
{
	const struct tracing_map_ops *map_ops = NULL;
	struct hist_trigger_data *hist_data;
	struct hist_trigger_data *hist_data;
	int ret = 0;
	int ret = 0;


@@ -646,8 +733,11 @@ create_hist_data(unsigned int map_bits,
	if (ret)
	if (ret)
		goto free;
		goto free;


	if (need_tracing_map_ops(hist_data))
		map_ops = &hist_trigger_elt_comm_ops;

	hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
	hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
					    NULL, hist_data);
					    map_ops, hist_data);
	if (IS_ERR(hist_data->map)) {
	if (IS_ERR(hist_data->map)) {
		ret = PTR_ERR(hist_data->map);
		ret = PTR_ERR(hist_data->map);
		hist_data->map = NULL;
		hist_data->map = NULL;
@@ -758,6 +848,12 @@ hist_trigger_entry_print(struct seq_file *m,
			sprint_symbol(str, uval);
			sprint_symbol(str, uval);
			seq_printf(m, "%s: [%llx] %-55s",
			seq_printf(m, "%s: [%llx] %-55s",
				   key_field->field->name, uval, str);
				   key_field->field->name, uval, str);
		} else if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
			char *comm = elt->private_data;

			uval = *(u64 *)(key + key_field->offset);
			seq_printf(m, "%s: %-16s[%10llu]",
				   key_field->field->name, comm, uval);
		} else if (key_field->flags & HIST_FIELD_FL_STRING) {
		} else if (key_field->flags & HIST_FIELD_FL_STRING) {
			seq_printf(m, "%s: %-50s", key_field->field->name,
			seq_printf(m, "%s: %-50s", key_field->field->name,
				   (char *)(key + key_field->offset));
				   (char *)(key + key_field->offset));
@@ -877,6 +973,8 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
		flags_str = "sym";
		flags_str = "sym";
	else if (hist_field->flags & HIST_FIELD_FL_SYM_OFFSET)
	else if (hist_field->flags & HIST_FIELD_FL_SYM_OFFSET)
		flags_str = "sym-offset";
		flags_str = "sym-offset";
	else if (hist_field->flags & HIST_FIELD_FL_EXECNAME)
		flags_str = "execname";


	return flags_str;
	return flags_str;
}
}