From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933575AbbI3TLL (ORCPT ); Wed, 30 Sep 2015 15:11:11 -0400 Received: from mga09.intel.com ([134.134.136.24]:26609 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932874AbbI3TLG (ORCPT ); Wed, 30 Sep 2015 15:11:06 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.17,613,1437462000"; d="scan'208";a="781188417" From: Tom Zanussi To: rostedt@goodmis.org Cc: daniel.wagner@bmw-carit.de, masami.hiramatsu.pt@hitachi.com, namhyung@kernel.org, josh@joshtriplett.org, andi@firstfloor.org, mathieu.desnoyers@efficios.com, peterz@infradead.org, linux-kernel@vger.kernel.org, Tom Zanussi Subject: [PATCH v10 12/26] tracing: Add hist trigger support for user-defined sorting ('sort=' param) Date: Wed, 30 Sep 2015 14:09:58 -0500 Message-Id: <3aa588feada1aa2a64ee954fb3886bc107ec5144.1443637631.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 List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Allow users to specify keys and/or values to sort on. With this addition, keys and values specified using the 'keys=' and 'vals=' keywords can be used to sort the hist trigger output via a new 'sort=' keyword. If multiple sort keys are specified, the output will be sorted using the second key as a secondary sort key, etc. The default sort order is ascending; if the user wants a different sort order, '.descending' can be appended to the specific sort key. Before this addition, output was always sorted by 'hitcount' in ascending order. This expands the hist trigger syntax from this: # echo hist:keys=xxx:vals=yyy \ [ if filter] > event/trigger to this: # echo hist:keys=xxx:vals=yyy:sort=zzz.descending \ [ if filter] > event/trigger Signed-off-by: Tom Zanussi --- kernel/trace/trace.c | 6 ++- kernel/trace/trace_events_hist.c | 101 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 7380d8d..5e23742 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3789,6 +3789,7 @@ static const char readme_msg[] = " hist trigger\t- If set, event hits are aggregated into a hash table\n" "\t Format: hist:keys=[,field2,...]\n" "\t [:values=]\n\n" "\t When a matching event is hit, an entry is added to a hash\n" @@ -3797,7 +3798,10 @@ static const char readme_msg[] = "\t correspond to fields in the event's format description. Keys\n" "\t can be any field. Compound keys consisting of up to two\n" "\t fields can be specified by the 'keys' keyword. Values must\n" - "\t correspond to numeric fields. The 'size' parameter can be\n" + "\t correspond to numeric fields. Sort keys consisting of up to\n" + "\t two fields can be specified using the 'sort' keyword. The\n" + "\t sort direction can be modified by appending '.descending' or\n" + "\t '.ascending' to a sort field. The 'size' parameter can be\n" "\t used to specify more or fewer than the default 2048 entries\n" "\t for the hashtable size.\n\n" "\t Reading the 'hist' file for the event will dump the hash\n" diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 112b8a2..4fc3136 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -77,6 +77,7 @@ enum hist_field_flags { struct hist_trigger_attrs { char *keys_str; char *vals_str; + char *sort_key_str; unsigned int map_bits; }; @@ -156,6 +157,7 @@ static int parse_map_size(char *str) static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs) { + kfree(attrs->sort_key_str); kfree(attrs->keys_str); kfree(attrs->vals_str); kfree(attrs); @@ -180,6 +182,8 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str) !strncmp(str, "vals", strlen("vals")) || !strncmp(str, "val", strlen("val"))) attrs->vals_str = kstrdup(str, GFP_KERNEL); + else if (!strncmp(str, "sort", strlen("sort"))) + attrs->sort_key_str = kstrdup(str, GFP_KERNEL); else if (!strncmp(str, "size", strlen("size"))) { int map_bits = parse_map_size(str); @@ -420,12 +424,88 @@ static int create_hist_fields(struct hist_trigger_data *hist_data, return ret; } +static int is_descending(const char *str) +{ + if (!str) + return 0; + + if (!strcmp(str, "descending")) + return 1; + + if (!strcmp(str, "ascending")) + return 0; + + return -EINVAL; +} + static int create_sort_keys(struct hist_trigger_data *hist_data) { + char *fields_str = hist_data->attrs->sort_key_str; + struct ftrace_event_field *field = NULL; + struct tracing_map_sort_key *sort_key; + unsigned int i, j; int ret = 0; hist_data->n_sort_keys = 1; /* sort_keys[0] is always hitcount */ + if (!fields_str) + goto out; + + strsep(&fields_str, "="); + if (!fields_str) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < TRACING_MAP_SORT_KEYS_MAX; i++) { + char *field_str, *field_name; + + sort_key = &hist_data->sort_keys[i]; + + field_str = strsep(&fields_str, ","); + if (!field_str) { + if (i == 0) + ret = -EINVAL; + break; + } + + if ((i == TRACING_MAP_SORT_KEYS_MAX - 1) && fields_str) { + ret = -EINVAL; + break; + } + + field_name = strsep(&field_str, "."); + if (!field_name) { + ret = -EINVAL; + break; + } + + if (!strcmp(field_name, "hitcount")) { + ret = is_descending(field_str); + if (ret < 0) + break; + sort_key->descending = ret; + continue; + } + + for (j = 1; j < hist_data->n_fields; j++) { + field = hist_data->fields[j]->field; + if (field && !strcmp(field_name, field->name)) { + sort_key->field_idx = j; + ret = is_descending(field_str); + if (ret < 0) + goto out; + sort_key->descending = ret; + break; + } + } + if (j == hist_data->n_fields) { + ret = -EINVAL; + break; + } + } + hist_data->n_sort_keys = i; + out: return ret; } @@ -737,7 +817,26 @@ static int event_hist_trigger_print(struct seq_file *m, } seq_puts(m, ":sort="); - seq_puts(m, "hitcount"); + + for (i = 0; i < hist_data->n_sort_keys; i++) { + struct tracing_map_sort_key *sort_key; + + sort_key = &hist_data->sort_keys[i]; + + if (i > 0) + seq_puts(m, ","); + + if (sort_key->field_idx == HITCOUNT_IDX) + seq_puts(m, "hitcount"); + else { + unsigned int idx = sort_key->field_idx; + + hist_field_print(m, hist_data->fields[idx]); + } + + if (sort_key->descending) + seq_puts(m, ".descending"); + } seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); -- 1.9.3