From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756745AbcKVUVL (ORCPT ); Tue, 22 Nov 2016 15:21:11 -0500 Received: from mx1.redhat.com ([209.132.183.28]:54516 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756480AbcKVUU6 (ORCPT ); Tue, 22 Nov 2016 15:20:58 -0500 From: Luiz Capitulino To: rostedt@goodmis.org Cc: linux-kernel@vger.kernel.org Subject: [PATCH 2/2] trace-cmd record: add --cpu-list option Date: Tue, 22 Nov 2016 15:20:52 -0500 Message-Id: <1479846052-8020-3-git-send-email-lcapitulino@redhat.com> In-Reply-To: <1479846052-8020-1-git-send-email-lcapitulino@redhat.com> References: <1479846052-8020-1-git-send-email-lcapitulino@redhat.com> X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Tue, 22 Nov 2016 20:20:57 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org With --cpu-list you can do: # trace-cmd record --cpu-list 1,4,10-15 [...] Which is much more human friendly than -M. Support for --cpu-list is implemented by dynamically allocating a cpu_set_t object and setting the parsed CPUs. Using the CPU_SET API allows for more robost error detection. Signed-off-by: Luiz Capitulino --- Documentation/trace-cmd-record.1.txt | 4 + trace-record.c | 208 +++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt index b80520e..d7e806a 100644 --- a/Documentation/trace-cmd-record.1.txt +++ b/Documentation/trace-cmd-record.1.txt @@ -304,6 +304,10 @@ OPTIONS executed will not be changed. This is useful if you want to monitor the output of the command being executed, but not see the output from trace-cmd. +*--cpu-list list*:: + List of CPUs to be traced. The "list" argument can be comma separated + (eg. 1,2,4,5), a range (eg. 1-10) or a mix of the two (eg. 1,2,10-15). + EXAMPLES -------- diff --git a/trace-record.c b/trace-record.c index 0f1f2c4..49d76db 100644 --- a/trace-record.c +++ b/trace-record.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -2080,6 +2081,208 @@ static void update_pid_event_filters(struct buffer_instance *instance) update_event_filters(instance); } +struct cpuset { + int nr_cpus; + size_t size; + cpu_set_t *set; +}; + +static void set_cpu(int cpu, struct cpuset *set) +{ + if (cpu < 0 || cpu > set->nr_cpus - 1) + die("invalid cpu in range"); + CPU_SET_S(cpu, set->size, set->set); +} + +static int read_cpu_nr(const char *str, int *ret) +{ + char *endptr; + int val; + + errno = 0; + val = strtol(str, &endptr, 10); + + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) + || (errno != 0 && val == 0)) + return -1; + + if (endptr == str) + return -1; + + if (*endptr != '\0') + return -1; + + *ret = val; + return 0; +} + +static int parse_single_cpu(const char *str, struct cpuset *set) +{ + int cpu, err; + + err = read_cpu_nr(str, &cpu); + if (err) + return -1; + + set_cpu(cpu, set); + return 0; +} + +static int parse_range_one(char *str, char **saveptr) +{ + int err, cpu; + char *p; + + p = strtok_r(str, "-", saveptr); + if (!p) + return -1; + + err = read_cpu_nr(p, &cpu); + if (err) + return -1; + + return cpu; +} + +static int parse_range(const char *str, int *begin, int *end) +{ + char *saveptr, *range; + int ret; + + range = strdup(str); + if (!range) + return -1; + + ret = parse_range_one(range, &saveptr); + if (ret < 0) + goto out; + *begin = ret; + + ret = parse_range_one(NULL, &saveptr); + if (ret < 0) + goto out; + *end = ret; + +out: + free(range); + return ret < 0 ? -1 : 0; +} + +static int parse_cpu_range(const char *str, struct cpuset *set) +{ + int i, ret, begin, end; + + ret = parse_range(str, &begin, &end); + if (ret < 0 || begin > end) + return -1; + + for (i = begin; i <= end; i++) + set_cpu(i, set); + + return 0; +} + +static int has_range(const char *str) +{ + return strchr(str, '-') != NULL; +} + +static int parse_cpu_list(const char *cpu_list, struct cpuset *set) +{ + char *saveptr, *str, *p; + int err = 0; + + str = strdup(cpu_list); + if (!str) + return -1; + + p = strtok_r(str, ",", &saveptr); + while (p) { + if (has_range(p)) + err = parse_cpu_range(p, set); + else + err = parse_single_cpu(p, set); + if (err) + goto out; + p = strtok_r(NULL, ",", &saveptr); + } + +out: + free(str); + return err; +} + +static int val_to_char(int v) +{ + if (v >= 0 && v < 10) + return '0' + v; + else if (v >= 10 && v < 16) + return ('a' - 10) + v; + else + return -1; +} + +/* From util-linux */ +static char *cpu_set_to_str(char *str, size_t len, struct cpuset *set) +{ + char *ptr = str; + char *ret = NULL; + int cpu; + + for (cpu = (8 * set->size) - 4; cpu >= 0; cpu -= 4) { + char val = 0; + + if (len == (size_t) (ptr - str)) + break; + + if (CPU_ISSET_S(cpu, set->size, set->set)) + val |= 1; + if (CPU_ISSET_S(cpu + 1, set->size, set->set)) + val |= 2; + if (CPU_ISSET_S(cpu + 2, set->size, set->set)) + val |= 4; + if (CPU_ISSET_S(cpu + 3, set->size, set->set)) + val |= 8; + + if (!ret && val) + ret = ptr; + *ptr++ = val_to_char(val); + } + + *ptr = '\0'; + return ret ? ret : ptr - 1; +} + +static char *alloc_mask_from_list(const char *cpu_list) +{ + struct cpuset set; + char *str; + int ret; + + set.nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + if (set.nr_cpus < 0) + die("can't get number of processors\n"); + + set.set = CPU_ALLOC(set.nr_cpus); + if (!set.set) + die("can't allocate cpu_set\n"); + + set.size = CPU_ALLOC_SIZE(set.nr_cpus); + CPU_ZERO_S(set.size, set.set); + + ret = parse_cpu_list(cpu_list, &set); + if (ret < 0) + die("invalid cpu list specified"); + + str = malloc(CPUMASK_STR_MAX); + if (!str) + die("can't allocate memory\n"); + + cpu_set_to_str(str, CPUMASK_STR_MAX, &set); + CPU_FREE(set.set); + + return str; +} static char *alloc_mask_from_hex(const char *str) { @@ -4137,6 +4340,7 @@ enum { OPT_nosplice = 253, OPT_funcstack = 254, OPT_date = 255, + OPT_cpulist = 256, }; void trace_record (int argc, char **argv) @@ -4338,6 +4542,7 @@ void trace_record (int argc, char **argv) {"stderr", no_argument, NULL, OPT_stderr}, {"by-comm", no_argument, NULL, OPT_bycomm}, {"ts-offset", required_argument, NULL, OPT_tsoffset}, + {"cpu-list", required_argument, NULL, OPT_cpulist}, {"max-graph-depth", required_argument, NULL, OPT_max_graph_depth}, {"debug", no_argument, NULL, OPT_debug}, {"help", no_argument, NULL, '?'}, @@ -4546,6 +4751,9 @@ void trace_record (int argc, char **argv) case 'M': instance->cpumask = alloc_mask_from_hex(optarg); break; + case OPT_cpulist: + instance->cpumask = alloc_mask_from_list(optarg); + break; case 't': if (extract) topt = 1; /* Extract top instance also */ -- 2.5.5