Received: by 10.223.164.202 with SMTP id h10csp685248wrb; Thu, 9 Nov 2017 12:40:50 -0800 (PST) X-Google-Smtp-Source: ABhQp+TC23zg8RgGQA5w1Q4wLpz8WpoWw83Ly/tlPTINVeiaa3uh33yjAQj1QV+Wdt4UqdRPwLi2 X-Received: by 10.84.128.99 with SMTP id 90mr1576186pla.171.1510260050525; Thu, 09 Nov 2017 12:40:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1510260050; cv=none; d=google.com; s=arc-20160816; b=VJEHRBGTBZarVRYtSVHNkale20G57ZWnzfg0ifl0nWXr7m6oKC7mYzXKrQZ15qCpOb WQ6v/Lc7mXNy5LLG5eierjlAeoP9jFaMTrw6iaEORR9AR3CCtEwtZYCeFDTbXuqEWd1q J27cdbOosnIu+Or85Y6gBMyZFwRuRVaX48b3HmMLE2bE2jgM/iQMySnilWt7Da+DIn+X 3+ZXFqDDtxd9LFaZnxS9nvgwHrslUdHE8rkcPfpuyGPqVd3MwG5ubBXw6wf1YJVqw1JM yzSNvtZJzSY3E6G9sJlRNnTkMfAvUFEo9DeHqVq+8CC/9fxIFdxFSE39xl+eE9cHg0VS GVLA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:references :in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=iWFZQvaWldg2nWQaS2Yc3C8ZFJpr+pp9c+YyEfihod4=; b=ViLXFZpBpPIlqZwe9a40HnHU/tS/mtVosAJDGnXP/+c00ajuTo58NgzebtrkZ/yJh0 cZDuTUrUcym332jiWGCv1j1qwnLXGSLp++dT4Ps0S4zNAmhbidxaIRCa5x53kpcvQXYB FjBVpUG2MVoIOupNIsIi7LdfOL7bl5rdjmpvVvI9o1hDmZE29Xzu+uckTeJ7l6jj8Xa2 /PYKToPwRnATolNkUJNXeJdJ6EwLjh8IspnLZEkACtjrq+BzmEis5CcPfnUu9LQZLUF9 AtP9F2zac9CmgDIWobsoTWfEaNaSqPXa9CiWEQIVXbzfSZ3XttscKvtcYJUmUWk3wDGS odWQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a5si6993271plh.517.2017.11.09.12.40.38; Thu, 09 Nov 2017 12:40:50 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755042AbdKIUf2 (ORCPT + 83 others); Thu, 9 Nov 2017 15:35:28 -0500 Received: from mga02.intel.com ([134.134.136.20]:17455 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754999AbdKIUfX (ORCPT ); Thu, 9 Nov 2017 15:35:23 -0500 Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2017 12:35:22 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.44,370,1505804400"; d="scan'208";a="5892175" Received: from dkusalov-mobl4.amr.corp.intel.com (HELO localhost) ([10.254.117.34]) by orsmga002.jf.intel.com with ESMTP; 09 Nov 2017 12:35:21 -0800 From: Tom Zanussi To: rostedt@goodmis.org Cc: tglx@linutronix.de, mhiramat@kernel.org, namhyung@kernel.org, vedang.patel@intel.com, bigeasy@linutronix.de, joel.opensrc@gmail.com, joelaf@google.com, mathieu.desnoyers@efficios.com, baohong.liu@intel.com, rajvi.jingar@intel.com, julia@ni.com, linux-kernel@vger.kernel.org, linux-rt-users@vger.kernel.org, Tom Zanussi Subject: [PATCH v5 22/37] tracing: Add variable reference handling to hist triggers Date: Thu, 9 Nov 2017 14:33:53 -0600 Message-Id: <840af62b520b8f0004e00ca2edfb199aeafc5b3a.1510252666.git.tom.zanussi@linux.intel.com> X-Mailer: git-send-email 1.9.3 In-Reply-To: References: In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add the necessary infrastructure to allow the variables defined on one event to be referenced in another. This allows variables set by a previous event to be referenced and used in expressions combining the variable values saved by that previous event and the event fields of the current event. For example, here's how a latency can be calculated and saved into yet another variable named 'wakeup_lat': # echo 'hist:keys=pid,prio:ts0=$common_timestamp ... # echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp-$ts0 ... In the first event, the event's timetamp is saved into the variable ts0. In the next line, ts0 is subtracted from the second event's timestamp to produce the latency. Further users of variable references will be described in subsequent patches, such as for instance how the 'wakeup_lat' variable above can be displayed in a latency histogram. Signed-off-by: Tom Zanussi --- kernel/trace/trace.c | 2 + kernel/trace/trace.h | 3 + kernel/trace/trace_events_hist.c | 706 +++++++++++++++++++++++++++++++++--- kernel/trace/trace_events_trigger.c | 6 + 4 files changed, 660 insertions(+), 57 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 969e247..f667a78 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7804,6 +7804,7 @@ static int instance_mkdir(const char *name) INIT_LIST_HEAD(&tr->systems); INIT_LIST_HEAD(&tr->events); + INIT_LIST_HEAD(&tr->hist_vars); if (allocate_trace_buffers(tr, trace_buf_size) < 0) goto out_free_tr; @@ -8554,6 +8555,7 @@ __init static int tracer_alloc_buffers(void) INIT_LIST_HEAD(&global_trace.systems); INIT_LIST_HEAD(&global_trace.events); + INIT_LIST_HEAD(&global_trace.hist_vars); list_add(&global_trace.list, &ftrace_trace_arrays); apply_trace_boot_options(); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 88a6fe3..52667dd 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -273,6 +273,7 @@ struct trace_array { int function_enabled; #endif int time_stamp_abs_ref; + struct list_head hist_vars; }; enum { @@ -1547,6 +1548,8 @@ extern int save_named_trigger(const char *name, extern void unpause_named_trigger(struct event_trigger_data *data); extern void set_named_trigger_data(struct event_trigger_data *data, struct event_trigger_data *named_data); +extern struct event_trigger_data * +get_named_trigger_data(struct event_trigger_data *data); extern int register_event_command(struct event_command *cmd); extern int unregister_event_command(struct event_command *cmd); extern int register_trigger_hist_enable_disable_cmds(void); diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 14fd84f..75494a8 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -59,7 +59,12 @@ struct hist_field { struct hist_trigger_data *hist_data; struct hist_var var; enum field_op_id operator; + char *system; + char *event_name; char *name; + unsigned int var_idx; + unsigned int var_ref_idx; + bool read_once; }; static u64 hist_field_none(struct hist_field *field, @@ -214,6 +219,7 @@ enum hist_field_flags { HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11, HIST_FIELD_FL_VAR = 1 << 12, HIST_FIELD_FL_EXPR = 1 << 13, + HIST_FIELD_FL_VAR_REF = 1 << 14, }; struct var_defs { @@ -253,6 +259,8 @@ struct hist_trigger_data { struct tracing_map *map; bool enable_timestamps; bool remove; + struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; + unsigned int n_var_refs; }; static u64 hist_field_timestamp(struct hist_field *hist_field, @@ -271,10 +279,395 @@ static u64 hist_field_timestamp(struct hist_field *hist_field, return ts; } +struct hist_var_data { + struct list_head list; + struct hist_trigger_data *hist_data; +}; + +static struct hist_field * +check_field_for_var_ref(struct hist_field *hist_field, + struct hist_trigger_data *var_data, + unsigned int var_idx) +{ + struct hist_field *found = NULL; + + if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF) { + if (hist_field->var.idx == var_idx && + hist_field->var.hist_data == var_data) { + found = hist_field; + } + } + + return found; +} + +static struct hist_field * +check_field_for_var_refs(struct hist_trigger_data *hist_data, + struct hist_field *hist_field, + struct hist_trigger_data *var_data, + unsigned int var_idx, + unsigned int level) +{ + struct hist_field *found = NULL; + unsigned int i; + + if (level > 2) + return found; + + if (!hist_field) + return found; + + found = check_field_for_var_ref(hist_field, var_data, var_idx); + if (found) + return found; + + for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) { + struct hist_field *operand; + + operand = hist_field->operands[i]; + found = check_field_for_var_refs(hist_data, operand, var_data, + var_idx, level + 1); + if (found) + return found; + } + + return found; +} + +static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data, + struct hist_trigger_data *var_data, + unsigned int var_idx) +{ + struct hist_field *hist_field, *found = NULL; + unsigned int i; + + for_each_hist_field(i, hist_data) { + hist_field = hist_data->fields[i]; + found = check_field_for_var_refs(hist_data, hist_field, + var_data, var_idx, 0); + if (found) + return found; + } + + return found; +} + +static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data, + unsigned int var_idx) +{ + struct trace_array *tr = hist_data->event_file->tr; + struct hist_field *found = NULL; + struct hist_var_data *var_data; + + found = find_var_ref(hist_data, hist_data, var_idx); + if (found) + return found; + + list_for_each_entry(var_data, &tr->hist_vars, list) { + found = find_var_ref(var_data->hist_data, hist_data, var_idx); + if (found) + break; + } + + return found; +} + +static bool check_var_refs(struct hist_trigger_data *hist_data) +{ + struct hist_field *field; + bool found = false; + int i; + + for_each_hist_field(i, hist_data) { + field = hist_data->fields[i]; + if (field && field->flags & HIST_FIELD_FL_VAR) { + if (find_any_var_ref(hist_data, field->var.idx)) { + found = true; + break; + } + } + } + + return found; +} + +static struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data) +{ + struct trace_array *tr = hist_data->event_file->tr; + struct hist_var_data *var_data, *found = NULL; + + list_for_each_entry(var_data, &tr->hist_vars, list) { + if (var_data->hist_data == hist_data) { + found = var_data; + break; + } + } + + return found; +} + +static bool field_has_hist_vars(struct hist_field *hist_field, + unsigned int level) +{ + int i; + + if (level > 2) + return false; + + if (!hist_field) + return false; + + if (hist_field->flags & HIST_FIELD_FL_VAR || + hist_field->flags & HIST_FIELD_FL_VAR_REF) + return true; + + for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) { + struct hist_field *operand; + + operand = hist_field->operands[i]; + if (field_has_hist_vars(operand, level + 1)) + return true; + } + + return false; +} + +static bool has_hist_vars(struct hist_trigger_data *hist_data) +{ + struct hist_field *hist_field; + int i; + + for_each_hist_field(i, hist_data) { + hist_field = hist_data->fields[i]; + if (field_has_hist_vars(hist_field, 0)) + return true; + } + + return false; +} + +static int save_hist_vars(struct hist_trigger_data *hist_data) +{ + struct trace_array *tr = hist_data->event_file->tr; + struct hist_var_data *var_data; + + var_data = find_hist_vars(hist_data); + if (var_data) + return 0; + + if (trace_array_get(tr) < 0) + return -ENODEV; + + var_data = kzalloc(sizeof(*var_data), GFP_KERNEL); + if (!var_data) { + trace_array_put(tr); + return -ENOMEM; + } + + var_data->hist_data = hist_data; + list_add(&var_data->list, &tr->hist_vars); + + return 0; +} + +static void remove_hist_vars(struct hist_trigger_data *hist_data) +{ + struct trace_array *tr = hist_data->event_file->tr; + struct hist_var_data *var_data; + + var_data = find_hist_vars(hist_data); + if (!var_data) + return; + + if (WARN_ON(check_var_refs(hist_data))) + return; + + list_del(&var_data->list); + + kfree(var_data); + + trace_array_put(tr); +} + +static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, + const char *var_name) +{ + struct hist_field *hist_field, *found = NULL; + int i; + + for_each_hist_field(i, hist_data) { + hist_field = hist_data->fields[i]; + if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR && + strcmp(hist_field->var.name, var_name) == 0) { + found = hist_field; + break; + } + } + + return found; +} + +static struct hist_field *find_var(struct trace_event_file *file, + const char *var_name) +{ + struct hist_trigger_data *hist_data; + struct event_trigger_data *test; + struct hist_field *hist_field; + + list_for_each_entry_rcu(test, &file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + hist_data = test->private_data; + hist_field = find_var_field(hist_data, var_name); + if (hist_field) + return hist_field; + } + } + + return NULL; +} + +static struct trace_event_file *find_var_file(struct trace_array *tr, + char *system, + char *event_name, + char *var_name) +{ + struct hist_trigger_data *var_hist_data; + struct hist_var_data *var_data; + struct trace_event_call *call; + struct trace_event_file *file, *found = NULL; + const char *name; + + list_for_each_entry(var_data, &tr->hist_vars, list) { + var_hist_data = var_data->hist_data; + file = var_hist_data->event_file; + call = file->event_call; + name = trace_event_name(call); + + if (!system || !event_name) { + if (find_var(file, var_name)) { + if (found) { + return NULL; + } + + found = file; + } + continue; + } + + if (strcmp(event_name, name) != 0) + continue; + if (strcmp(system, call->class->system) != 0) + continue; + + found = file; + break; + } + + return found; +} + +static struct hist_field *find_file_var(struct trace_event_file *file, + const char *var_name) +{ + struct hist_trigger_data *test_data; + struct event_trigger_data *test; + struct hist_field *hist_field; + + list_for_each_entry_rcu(test, &file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + test_data = test->private_data; + hist_field = find_var_field(test_data, var_name); + if (hist_field) + return hist_field; + } + } + + return NULL; +} + +static struct hist_field *find_event_var(struct hist_trigger_data *hist_data, + char *system, + char *event_name, + char *var_name) +{ + struct trace_array *tr = hist_data->event_file->tr; + struct hist_field *hist_field = NULL; + struct trace_event_file *file; + + file = find_var_file(tr, system, event_name, var_name); + if (!file) + return NULL; + + hist_field = find_file_var(file, var_name); + + return hist_field; +} + struct hist_elt_data { char *comm; + u64 *var_ref_vals; }; +static u64 hist_field_var_ref(struct hist_field *hist_field, + struct tracing_map_elt *elt, + struct ring_buffer_event *rbe, + void *event) +{ + struct hist_elt_data *elt_data; + u64 var_val = 0; + + elt_data = elt->private_data; + var_val = elt_data->var_ref_vals[hist_field->var_ref_idx]; + + return var_val; +} + +static bool resolve_var_refs(struct hist_trigger_data *hist_data, void *key, + u64 *var_ref_vals, bool self) +{ + struct hist_trigger_data *var_data; + struct tracing_map_elt *var_elt; + struct hist_field *hist_field; + unsigned int i, var_idx; + bool resolved = true; + u64 var_val = 0; + + for (i = 0; i < hist_data->n_var_refs; i++) { + hist_field = hist_data->var_refs[i]; + var_idx = hist_field->var.idx; + var_data = hist_field->var.hist_data; + + if (var_data == NULL) { + resolved = false; + break; + } + + if ((self && var_data != hist_data) || + (!self && var_data == hist_data)) + continue; + + var_elt = tracing_map_lookup(var_data->map, key); + if (!var_elt) { + resolved = false; + break; + } + + if (!tracing_map_var_set(var_elt, var_idx)) { + resolved = false; + break; + } + + if (self || !hist_field->read_once) + var_val = tracing_map_read_var(var_elt, var_idx); + else + var_val = tracing_map_read_var_once(var_elt, var_idx); + + var_ref_vals[i] = var_val; + } + + return resolved; +} + static const char *hist_field_name(struct hist_field *field, unsigned int level) { @@ -289,8 +682,20 @@ static const char *hist_field_name(struct hist_field *field, field_name = hist_field_name(field->operands[0], ++level); else if (field->flags & HIST_FIELD_FL_TIMESTAMP) field_name = "$common_timestamp"; - else if (field->flags & HIST_FIELD_FL_EXPR) - field_name = field->name; + else if (field->flags & HIST_FIELD_FL_EXPR || + field->flags & HIST_FIELD_FL_VAR_REF) { + if (field->system) { + static char full_name[MAX_FILTER_STR_VAL]; + + strcat(full_name, field->system); + strcat(full_name, "."); + strcat(full_name, field->event_name); + strcat(full_name, "."); + strcat(full_name, field->name); + field_name = full_name; + } else + field_name = field->name; + } if (field_name == NULL) field_name = ""; @@ -594,6 +999,8 @@ static char *expr_str(struct hist_field *field, unsigned int level) return expr; } + if (field->operands[0]->flags & HIST_FIELD_FL_VAR_REF) + strcat(expr, "$"); strcat(expr, hist_field_name(field->operands[0], 0)); if (field->operands[0]->flags) { const char *flags_str = get_hist_field_flags(field->operands[0]); @@ -616,6 +1023,8 @@ static char *expr_str(struct hist_field *field, unsigned int level) return NULL; } + if (field->operands[1]->flags & HIST_FIELD_FL_VAR_REF) + strcat(expr, "$"); strcat(expr, hist_field_name(field->operands[1], 0)); if (field->operands[1]->flags) { const char *flags_str = get_hist_field_flags(field->operands[1]); @@ -695,6 +1104,11 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data, if (flags & HIST_FIELD_FL_EXPR) goto out; /* caller will populate */ + if (flags & HIST_FIELD_FL_VAR_REF) { + hist_field->fn = hist_field_var_ref; + goto out; + } + if (flags & HIST_FIELD_FL_HITCOUNT) { hist_field->fn = hist_field_counter; hist_field->size = sizeof(u64); @@ -788,6 +1202,77 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data) } } +static int init_var_ref(struct hist_field *ref_field, + struct hist_field *var_field, + char *system, char *event_name) +{ + int err = 0; + + ref_field->var.idx = var_field->var.idx; + ref_field->var.hist_data = var_field->hist_data; + ref_field->size = var_field->size; + ref_field->is_signed = var_field->is_signed; + + if (system) { + ref_field->system = kstrdup(system, GFP_KERNEL); + if (!ref_field->system) + return -ENOMEM; + } + + if (event_name) { + ref_field->event_name = kstrdup(event_name, GFP_KERNEL); + if (!ref_field->event_name) { + err = -ENOMEM; + goto free; + } + } + + ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL); + if (!ref_field->name) { + err = -ENOMEM; + goto free; + } + + ref_field->type = kstrdup(var_field->type, GFP_KERNEL); + if (!ref_field->type) { + err = -ENOMEM; + goto free; + } + out: + return err; + free: + kfree(ref_field->system); + kfree(ref_field->event_name); + kfree(ref_field->name); + + goto out; +} + +static struct hist_field *create_var_ref(struct hist_field *var_field, + char *system, char *event_name) +{ + unsigned long flags = HIST_FIELD_FL_VAR_REF; + struct hist_field *ref_field; + + ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL); + if (ref_field) { + if (init_var_ref(ref_field, var_field, system, event_name)) { + destroy_hist_field(ref_field, 0); + return NULL; + } + } + + return ref_field; +} + +static bool is_var_ref(char *var_name) +{ + if (!var_name || strlen(var_name) < 2 || var_name[0] != '$') + return false; + + return true; +} + static char *field_name_from_var(struct hist_trigger_data *hist_data, char *var_name) { @@ -799,7 +1284,7 @@ static char *field_name_from_var(struct hist_trigger_data *hist_data, if (strcmp(var_name, name) == 0) { field = hist_data->attrs->var_defs.expr[i]; - if (contains_operator(field)) + if (contains_operator(field) || is_var_ref(field)) continue; return field; } @@ -811,11 +1296,32 @@ static char *field_name_from_var(struct hist_trigger_data *hist_data, static char *local_field_var_ref(struct hist_trigger_data *hist_data, char *var_name) { + if (!is_var_ref(var_name)) + return NULL; + var_name++; return field_name_from_var(hist_data, var_name); } +static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data, + char *system, char *event_name, + char *var_name) +{ + struct hist_field *var_field = NULL, *ref_field = NULL; + + if (!is_var_ref(var_name)) + return NULL; + + var_name++; + + var_field = find_event_var(hist_data, system, event_name, var_name); + if (var_field) + ref_field = create_var_ref(var_field, system, event_name); + + return ref_field; +} + static struct ftrace_event_field * parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, char *field_str, unsigned long *flags) @@ -872,13 +1378,38 @@ struct hist_field *parse_atom(struct hist_trigger_data *hist_data, struct trace_event_file *file, char *str, unsigned long *flags, char *var_name) { - char *s; + char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str; struct ftrace_event_field *field = NULL; struct hist_field *hist_field = NULL; int ret = 0; - s = local_field_var_ref(hist_data, str); - if (s) + s = strchr(str, '.'); + if (s) { + s = strchr(++s, '.'); + if (s) { + ref_system = strsep(&str, "."); + if (!str) { + ret = -EINVAL; + goto out; + } + ref_event = strsep(&str, "."); + if (!str) { + ret = -EINVAL; + goto out; + } + ref_var = str; + } + } + + s = local_field_var_ref(hist_data, ref_var); + if (!s) { + hist_field = parse_var_ref(hist_data, ref_system, ref_event, ref_var); + if (hist_field) { + hist_data->var_refs[hist_data->n_var_refs] = hist_field; + hist_field->var_ref_idx = hist_data->n_var_refs++; + return hist_field; + } + } else str = s; field = parse_field(hist_data, file, str, flags); @@ -1052,6 +1583,9 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data, goto free; } + operand1->read_once = true; + operand2->read_once = true; + expr->operands[0] = operand1; expr->operands[1] = operand2; expr->operator = field_op; @@ -1098,43 +1632,6 @@ static int create_hitcount_val(struct hist_trigger_data *hist_data) return 0; } -static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, - const char *var_name) -{ - struct hist_field *hist_field, *found = NULL; - int i; - - for_each_hist_field(i, hist_data) { - hist_field = hist_data->fields[i]; - if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR && - strcmp(hist_field->var.name, var_name) == 0) { - found = hist_field; - break; - } - } - - return found; -} - -static struct hist_field *find_var(struct trace_event_file *file, - const char *var_name) -{ - struct hist_trigger_data *hist_data; - struct event_trigger_data *test; - struct hist_field *hist_field; - - list_for_each_entry_rcu(test, &file->triggers, list) { - if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { - hist_data = test->private_data; - hist_field = find_var_field(hist_data, var_name); - if (hist_field) - return hist_field; - } - } - - return NULL; -} - static int __create_val_field(struct hist_trigger_data *hist_data, unsigned int val_idx, struct trace_event_file *file, @@ -1263,6 +1760,12 @@ static int create_key_field(struct hist_trigger_data *hist_data, goto out; } + if (hist_field->flags & HIST_FIELD_FL_VAR_REF) { + destroy_hist_field(hist_field, 0); + ret = -EINVAL; + goto out; + } + key_size = hist_field->size; } @@ -1599,6 +2102,7 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data) hist_data->attrs = attrs; hist_data->remove = remove; + hist_data->event_file = file; ret = create_hist_fields(hist_data, file); if (ret) @@ -1621,12 +2125,6 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data) ret = create_tracing_map_fields(hist_data); if (ret) goto free; - - ret = tracing_map_init(hist_data->map); - if (ret) - goto free; - - hist_data->event_file = file; out: return hist_data; free: @@ -1641,12 +2139,17 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data) static void hist_trigger_elt_update(struct hist_trigger_data *hist_data, struct tracing_map_elt *elt, void *rec, - struct ring_buffer_event *rbe) + struct ring_buffer_event *rbe, + u64 *var_ref_vals) { + struct hist_elt_data *elt_data; struct hist_field *hist_field; unsigned int i, var_idx; u64 hist_val; + elt_data = elt->private_data; + elt_data->var_ref_vals = var_ref_vals; + for_each_hist_val_field(i, hist_data) { hist_field = hist_data->fields[i]; hist_val = hist_field->fn(hist_field, elt, rbe, rec); @@ -1698,6 +2201,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec, struct hist_trigger_data *hist_data = data->private_data; bool use_compound_key = (hist_data->n_keys > 1); unsigned long entries[HIST_STACKTRACE_DEPTH]; + u64 var_ref_vals[TRACING_MAP_VARS_MAX]; char compound_key[HIST_KEY_SIZE_MAX]; struct tracing_map_elt *elt = NULL; struct stack_trace stacktrace; @@ -1737,9 +2241,15 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec, if (use_compound_key) key = compound_key; + if (hist_data->n_var_refs && + !resolve_var_refs(hist_data, key, var_ref_vals, false)) + return; + elt = tracing_map_insert(hist_data->map, key); - if (elt) - hist_trigger_elt_update(hist_data, elt, rec, rbe); + if (!elt) + return; + + hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals); } static void hist_trigger_stacktrace_print(struct seq_file *m, @@ -1954,8 +2464,11 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) seq_puts(m, "$common_timestamp"); - else if (field_name) + else if (field_name) { + if (hist_field->flags & HIST_FIELD_FL_VAR_REF) + seq_putc(m, '$'); seq_printf(m, "%s", field_name); + } if (hist_field->flags) { const char *flags_str = get_hist_field_flags(hist_field); @@ -2095,7 +2608,11 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops, if (!data->ref) { if (data->name) del_named_trigger(data); + trigger_data_free(data); + + remove_hist_vars(hist_data); + destroy_hist_data(hist_data); } } @@ -2308,23 +2825,55 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops, goto out; } - list_add_rcu(&data->list, &file->triggers); ret++; - update_cond_flag(file); - if (hist_data->enable_timestamps) tracing_set_time_stamp_abs(file->tr, true); + out: + return ret; +} + +static int hist_trigger_enable(struct event_trigger_data *data, + struct trace_event_file *file) +{ + int ret = 0; + + list_add_tail_rcu(&data->list, &file->triggers); + + update_cond_flag(file); if (trace_event_trigger_enable_disable(file, 1) < 0) { list_del_rcu(&data->list); update_cond_flag(file); ret--; } - out: + return ret; } +static bool hist_trigger_check_refs(struct event_trigger_data *data, + struct trace_event_file *file) +{ + struct hist_trigger_data *hist_data = data->private_data; + struct event_trigger_data *test, *named_data = NULL; + + if (hist_data->attrs->name) + named_data = find_named_trigger(hist_data->attrs->name); + + list_for_each_entry_rcu(test, &file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + if (!hist_trigger_match(data, test, named_data, false)) + continue; + hist_data = test->private_data; + if (check_var_refs(hist_data)) + return true; + break; + } + } + + return false; +} + static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, struct event_trigger_data *data, struct trace_event_file *file) @@ -2357,11 +2906,30 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, } } +static bool hist_file_check_refs(struct trace_event_file *file) +{ + struct hist_trigger_data *hist_data; + struct event_trigger_data *test; + + list_for_each_entry_rcu(test, &file->triggers, list) { + if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { + hist_data = test->private_data; + if (check_var_refs(hist_data)) + return true; + } + } + + return false; +} + static void hist_unreg_all(struct trace_event_file *file) { struct event_trigger_data *test, *n; struct hist_trigger_data *hist_data; + if (hist_file_check_refs(file)) + return; + list_for_each_entry_safe(test, n, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { hist_data = test->private_data; @@ -2437,6 +3005,11 @@ static int event_hist_trigger_func(struct event_command *cmd_ops, } if (remove) { + if (hist_trigger_check_refs(trigger_data, file)) { + ret = -EBUSY; + goto out_free; + } + cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); ret = 0; goto out_free; @@ -2454,14 +3027,33 @@ static int event_hist_trigger_func(struct event_command *cmd_ops, goto out_free; } else if (ret < 0) goto out_free; + + if (get_named_trigger_data(trigger_data)) + goto enable; + + if (has_hist_vars(hist_data)) + save_hist_vars(hist_data); + + ret = tracing_map_init(hist_data->map); + if (ret) + goto out_unreg; +enable: + ret = hist_trigger_enable(trigger_data, file); + if (ret) + goto out_unreg; + /* Just return zero, not the number of registered triggers */ ret = 0; out: return ret; + out_unreg: + cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); out_free: if (cmd_ops->set_filter) cmd_ops->set_filter(NULL, trigger_data, NULL); + remove_hist_vars(hist_data); + kfree(trigger_data); destroy_hist_data(hist_data); diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 9b0fe31..a7a5bed 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -909,6 +909,12 @@ void set_named_trigger_data(struct event_trigger_data *data, data->named_data = named_data; } +struct event_trigger_data * +get_named_trigger_data(struct event_trigger_data *data) +{ + return data->named_data; +} + static void traceon_trigger(struct event_trigger_data *data, void *rec, struct ring_buffer_event *event) -- 1.9.3 From 1583307678825311137@xxx Mon Nov 06 09:17:48 +0000 2017 X-GM-THRID: 1583307678825311137 X-Gmail-Labels: Inbox,Category Promotions,HistoricalUnread