2015-02-27 10:19:33

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 0/7] perf core: Make some improvements and fixes

Hi,
Found some functions to improve and bugs to fix in perf.

This patchset is based on:
https://git.kernel.org/cgit/linux/kernel/git/acme/linux.git/ perf/core

For [PATCH v3 4/7] and [PATCH v3 5/7], I have checked that these
patches do not break shell autocompletion. Besides, for [PATCH v3 4/7],
--raw-dump without an argument still shows all the events.

Yunlong Song (7):
perf list: Sort the output of 'perf list' to view more clearly
perf list: Fix some inaccuracy problem when parsing the argument
perf list: Clean up the printing functions of hardware/software events
perf list: Extend raw-dump to certain kind of events
perf list: Avoid confusion of perf output and the next command prompt
perf record: Remove the '--(null)' long_name for --list-opts
perf core: Fix the bash completion problem of 'perf --*'

tools/perf/Documentation/perf-list.txt | 6 +
tools/perf/builtin-list.c | 27 ++---
tools/perf/perf-completion.sh | 6 +-
tools/perf/perf.c | 28 +++++
tools/perf/util/parse-events.c | 206 ++++++++++++++++++++++++---------
tools/perf/util/parse-events.h | 11 +-
tools/perf/util/parse-options.c | 5 +-
7 files changed, 210 insertions(+), 79 deletions(-)

--
1.8.5.5


2015-02-27 10:21:10

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 1/7] perf list: Sort the output of 'perf list' to view more clearly

Sort the output according to ASCII character list (using strcmp), which
supports both number sequence and alphabet sequence.

Example:

Before this patch:

$ perf list

List of pre-defined events (to be used in -e):
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
cache-references [Hardware event]
cache-misses [Hardware event]
branch-instructions OR branches [Hardware event]
branch-misses [Hardware event]
bus-cycles [Hardware event]
... ...

jbd2:jbd2_start_commit [Tracepoint event]
jbd2:jbd2_commit_locking [Tracepoint event]
jbd2:jbd2_run_stats [Tracepoint event]
block:block_rq_issue [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_bio_backmerge [Tracepoint event]
block:block_getrq [Tracepoint event]
... ...

After this patch:

$ perf list

List of pre-defined events (to be used in -e):
branch-instructions OR branches [Hardware event]
branch-misses [Hardware event]
bus-cycles [Hardware event]
cache-misses [Hardware event]
cache-references [Hardware event]
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
... ...

block:block_bio_backmerge [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_getrq [Tracepoint event]
block:block_rq_issue [Tracepoint event]
jbd2:jbd2_commit_locking [Tracepoint event]
jbd2:jbd2_run_stats [Tracepoint event]
jbd2:jbd2_start_commit [Tracepoint event]
... ...

Signed-off-by: Yunlong Song <[email protected]>
---
tools/perf/util/parse-events.c | 212 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 189 insertions(+), 23 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 109ba5c..41e196f 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1089,6 +1089,14 @@ static const char * const event_type_descriptors[] = {
"Hardware breakpoint",
};

+static int cmp_string(const void *a, const void *b)
+{
+ const char * const *as = a;
+ const char * const *bs = b;
+
+ return strcmp(*as, *bs);
+}
+
/*
* Print the events from <debugfs_mount_point>/tracing/events
*/
@@ -1100,11 +1108,21 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
+ char **evt_list = NULL;
+ unsigned int evt_i = 0, evt_num = 0;
+ bool evt_num_known = false;

+restart:
sys_dir = opendir(tracing_events_path);
if (!sys_dir)
return;

+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_enomem;
+ }
+
for_each_subsystem(sys_dir, sys_dirent, sys_next) {
if (subsys_glob != NULL &&
!strglobmatch(sys_dirent.d_name, subsys_glob))
@@ -1121,19 +1139,52 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
!strglobmatch(evt_dirent.d_name, event_glob))
continue;

- if (name_only) {
- printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name);
+ if (!evt_num_known) {
+ evt_num++;
continue;
}

snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name);
- printf(" %-50s [%s]\n", evt_path,
- event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+
+ evt_list[evt_i] = strdup(evt_path);
+ if (evt_list[evt_i] == NULL)
+ goto out_enomem;
+ evt_i++;
}
closedir(evt_dir);
}
closedir(sys_dir);
+
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
+ }
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num) {
+ if (name_only) {
+ printf("%s ", evt_list[evt_i++]);
+ continue;
+ }
+ printf(" %-50s [%s]\n", evt_list[evt_i++],
+ event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+ }
+ if (evt_num)
+ printf("\n");
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return;
+
+out_enomem:
+ printf("FATAL: not enough memory to print %s\n",
+ event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+ if (evt_list)
+ goto out_free;
}

/*
@@ -1218,20 +1269,61 @@ static void __print_events_type(u8 type, struct event_symbol *syms,
unsigned max)
{
char name[64];
- unsigned i;
+ unsigned int i, evt_i = 0, evt_num = 0;
+ char **evt_list = NULL;
+ bool evt_num_known = false;
+
+restart:
+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_enomem;
+ syms -= max;
+ }

for (i = 0; i < max ; i++, syms++) {
if (!is_event_supported(type, i))
continue;

+ if (!evt_num_known) {
+ evt_num++;
+ continue;
+ }
+
if (strlen(syms->alias))
snprintf(name, sizeof(name), "%s OR %s",
syms->symbol, syms->alias);
else
snprintf(name, sizeof(name), "%s", syms->symbol);

- printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
+ evt_list[evt_i] = strdup(name);
+ if (evt_list[evt_i] == NULL)
+ goto out_enomem;
+ evt_i++;
}
+
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
+ }
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num)
+ printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
+ if (evt_num)
+ printf("\n");
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return;
+
+out_enomem:
+ printf("FATAL: not enough memory to print %s\n", event_type_descriptors[type]);
+ if (evt_list)
+ goto out_free;
}

void print_events_type(u8 type)
@@ -1244,8 +1336,17 @@ void print_events_type(u8 type)

int print_hwcache_events(const char *event_glob, bool name_only)
{
- unsigned int type, op, i, printed = 0;
+ unsigned int type, op, i, evt_i = 0, evt_num = 0;
char name[64];
+ char **evt_list = NULL;
+ bool evt_num_known = false;
+
+restart:
+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_enomem;
+ }

for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
@@ -1263,27 +1364,66 @@ int print_hwcache_events(const char *event_glob, bool name_only)
type | (op << 8) | (i << 16)))
continue;

- if (name_only)
- printf("%s ", name);
- else
- printf(" %-50s [%s]\n", name,
- event_type_descriptors[PERF_TYPE_HW_CACHE]);
- ++printed;
+ if (!evt_num_known) {
+ evt_num++;
+ continue;
+ }
+
+ evt_list[evt_i] = strdup(name);
+ if (evt_list[evt_i] == NULL)
+ goto out_enomem;
+ evt_i++;
}
}
}

- if (printed)
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
+ }
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num) {
+ if (name_only) {
+ printf("%s ", evt_list[evt_i++]);
+ continue;
+ }
+ printf(" %-50s [%s]\n", evt_list[evt_i++],
+ event_type_descriptors[PERF_TYPE_HW_CACHE]);
+ }
+ if (evt_num)
printf("\n");
- return printed;
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return evt_num;
+
+out_enomem:
+ printf("FATAL: not enough memory to print %s\n", event_type_descriptors[PERF_TYPE_HW_CACHE]);
+ if (evt_list)
+ goto out_free;
+ return evt_num;
}

static void print_symbol_events(const char *event_glob, unsigned type,
struct event_symbol *syms, unsigned max,
bool name_only)
{
- unsigned i, printed = 0;
+ unsigned int i, evt_i = 0, evt_num = 0;
char name[MAX_NAME_LEN];
+ char **evt_list = NULL;
+ bool evt_num_known = false;
+
+restart:
+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_enomem;
+ syms -= max;
+ }

for (i = 0; i < max; i++, syms++) {

@@ -1295,23 +1435,49 @@ static void print_symbol_events(const char *event_glob, unsigned type,
if (!is_event_supported(type, i))
continue;

- if (name_only) {
- printf("%s ", syms->symbol);
+ if (!evt_num_known) {
+ evt_num++;
continue;
}

- if (strlen(syms->alias))
+ if (!name_only && strlen(syms->alias))
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
else
strncpy(name, syms->symbol, MAX_NAME_LEN);

- printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
-
- printed++;
+ evt_list[evt_i] = strdup(name);
+ if (evt_list[evt_i] == NULL)
+ goto out_enomem;
+ evt_i++;
}

- if (printed)
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
+ }
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num) {
+ if (name_only) {
+ printf("%s ", evt_list[evt_i++]);
+ continue;
+ }
+ printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
+ }
+ if (evt_num)
printf("\n");
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return;
+
+out_enomem:
+ printf("FATAL: not enough memory to print %s\n", event_type_descriptors[type]);
+ if (evt_list)
+ goto out_free;
}

/*
--
1.8.5.5

2015-02-27 10:21:09

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 2/7] perf list: Fix some inaccuracy problem when parsing the argument

If somebody happens to name an event with the beginning of 'tracepoint'
(e.g. tracepoint_foo), then it will never be showed with perf list
event_glob, thus we parse the argument 'tracepoint' more carefully for
accuracy.

Example:

Before this patch:

$ perf list tracepoint_foo:*

jbd2:jbd2_start_commit [Tracepoint event]
jbd2:jbd2_commit_locking [Tracepoint event]
jbd2:jbd2_run_stats [Tracepoint event]
block:block_rq_issue [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_bio_backmerge [Tracepoint event]
block:block_getrq [Tracepoint event]
... ...

As shown above, all of the tracepoint events are printed. In fact, the
command's real intention is to print the events of tracepoint_foo.

After this patch:

$ perf list tracepoint_foo:*

tracepoint_foo:tp_foo_enter [Tracepoint event]
tracepoint_foo:tp_foo_exit [Tracepoint event]

As shown above, only the events of tracepoint_foo are printed.

Signed-off-by: Yunlong Song <[email protected]>
---
tools/perf/builtin-list.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index ad8018e..2acbcf0 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -50,9 +50,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
}

for (i = 0; i < argc; ++i) {
- if (i)
- putchar('\n');
- if (strncmp(argv[i], "tracepoint", 10) == 0)
+ if (strcmp(argv[i], "tracepoint") == 0)
print_tracepoint_events(NULL, NULL, false);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)
--
1.8.5.5

2015-02-27 10:19:48

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 3/7] perf list: Clean up the printing functions of hardware/software events

Do not need print_events_type or __print_events_type for listing hw/sw
events, let print_symbol_events do its job instead. Moreover,
print_symbol_events can also handle event_glob and name_only.

Signed-off-by: Yunlong Song <[email protected]>
---
tools/perf/builtin-list.c | 6 ++--
tools/perf/util/parse-events.c | 80 ++----------------------------------------
tools/perf/util/parse-events.h | 11 +++++-
3 files changed, 17 insertions(+), 80 deletions(-)

diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 2acbcf0..8b323e0 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -54,10 +54,12 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
print_tracepoint_events(NULL, NULL, false);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)
- print_events_type(PERF_TYPE_HARDWARE);
+ print_symbol_events(NULL, PERF_TYPE_HARDWARE,
+ event_symbols_hw, PERF_COUNT_HW_MAX, false);
else if (strcmp(argv[i], "sw") == 0 ||
strcmp(argv[i], "software") == 0)
- print_events_type(PERF_TYPE_SOFTWARE);
+ print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
+ event_symbols_sw, PERF_COUNT_SW_MAX, false);
else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(NULL, false);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 41e196f..a6465de 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -20,11 +20,6 @@

#define MAX_NAME_LEN 100

-struct event_symbol {
- const char *symbol;
- const char *alias;
-};
-
#ifdef PARSER_DEBUG
extern int parse_events_debug;
#endif
@@ -39,7 +34,7 @@ static struct perf_pmu_event_symbol *perf_pmu_events_list;
*/
static int perf_pmu_events_list_num;

-static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
+struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = {
.symbol = "cpu-cycles",
.alias = "cycles",
@@ -82,7 +77,7 @@ static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
},
};

-static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
+struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
[PERF_COUNT_SW_CPU_CLOCK] = {
.symbol = "cpu-clock",
.alias = "",
@@ -1265,75 +1260,6 @@ static bool is_event_supported(u8 type, unsigned config)
return ret;
}

-static void __print_events_type(u8 type, struct event_symbol *syms,
- unsigned max)
-{
- char name[64];
- unsigned int i, evt_i = 0, evt_num = 0;
- char **evt_list = NULL;
- bool evt_num_known = false;
-
-restart:
- if (evt_num_known) {
- evt_list = zalloc(sizeof(char *) * evt_num);
- if (!evt_list)
- goto out_enomem;
- syms -= max;
- }
-
- for (i = 0; i < max ; i++, syms++) {
- if (!is_event_supported(type, i))
- continue;
-
- if (!evt_num_known) {
- evt_num++;
- continue;
- }
-
- if (strlen(syms->alias))
- snprintf(name, sizeof(name), "%s OR %s",
- syms->symbol, syms->alias);
- else
- snprintf(name, sizeof(name), "%s", syms->symbol);
-
- evt_list[evt_i] = strdup(name);
- if (evt_list[evt_i] == NULL)
- goto out_enomem;
- evt_i++;
- }
-
- if (!evt_num_known) {
- evt_num_known = true;
- goto restart;
- }
- qsort(evt_list, evt_num, sizeof(char *), cmp_string);
- evt_i = 0;
- while (evt_i < evt_num)
- printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
- if (evt_num)
- printf("\n");
-
-out_free:
- evt_num = evt_i;
- for (evt_i = 0; evt_i < evt_num; evt_i++)
- zfree(&evt_list[evt_i]);
- zfree(&evt_list);
- return;
-
-out_enomem:
- printf("FATAL: not enough memory to print %s\n", event_type_descriptors[type]);
- if (evt_list)
- goto out_free;
-}
-
-void print_events_type(u8 type)
-{
- if (type == PERF_TYPE_SOFTWARE)
- __print_events_type(type, event_symbols_sw, PERF_COUNT_SW_MAX);
- else
- __print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX);
-}
-
int print_hwcache_events(const char *event_glob, bool name_only)
{
unsigned int type, op, i, evt_i = 0, evt_num = 0;
@@ -1408,7 +1334,7 @@ out_enomem:
return evt_num;
}

-static void print_symbol_events(const char *event_glob, unsigned type,
+void print_symbol_events(const char *event_glob, unsigned type,
struct event_symbol *syms, unsigned max,
bool name_only)
{
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 39c3b57..52a2dda 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -116,7 +116,16 @@ void parse_events_update_lists(struct list_head *list_event,
void parse_events_error(void *data, void *scanner, char const *msg);

void print_events(const char *event_glob, bool name_only);
-void print_events_type(u8 type);
+
+struct event_symbol {
+ const char *symbol;
+ const char *alias;
+};
+extern struct event_symbol event_symbols_hw[];
+extern struct event_symbol event_symbols_sw[];
+void print_symbol_events(const char *event_glob, unsigned type,
+ struct event_symbol *syms, unsigned max,
+ bool name_only);
void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
bool name_only);
int print_hwcache_events(const char *event_glob, bool name_only);
--
1.8.5.5

2015-02-27 10:19:41

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 4/7] perf list: Extend raw-dump to certain kind of events

Extend 'perf list --raw-dump' to 'perf list --raw-dump [hw|sw|cache
|tracepoint|pmu|event_glob]' in order to show the raw-dump of a
certain kind of events rather than all of the events.

Example:

Before this patch:

$ perf list --raw-dump hw
branch-instructions branch-misses bus-cycles cache-misses
cache-references cpu-cycles instructions stalled-cycles-backend
stalled-cycles-frontend
alignment-faults context-switches cpu-clock cpu-migrations
emulation-faults major-faults minor-faults page-faults task-clock
...
...
writeback:writeback_thread_start writeback:writeback_thread_stop
writeback:writeback_wait_iff_congested
writeback:writeback_wake_background writeback:writeback_wake_thread

As shown above, all of the events are printed.

After this patch:

$ perf list --raw-dump hw
branch-instructions branch-misses bus-cycles cache-misses
cache-references cpu-cycles instructions stalled-cycles-backend
stalled-cycles-frontend

As shown above, only the hw events are printed.

Signed-off-by: Yunlong Song <[email protected]>
---
tools/perf/Documentation/perf-list.txt | 6 ++++++
tools/perf/builtin-list.c | 21 ++++++++-------------
2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index 3e2aec9..4692d27 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -127,6 +127,12 @@ To limit the list use:
One or more types can be used at the same time, listing the events for the
types specified.

+Support raw format:
+
+. '--raw-dump', shows the raw-dump of all the events.
+. '--raw-dump [hw|sw|cache|tracepoint|pmu|event_glob]', shows the raw-dump of
+ a certain kind of events.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-top[1],
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 8b323e0..af5bd05 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -36,41 +36,36 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)

setup_pager();

- if (raw_dump) {
- print_events(NULL, true);
- return 0;
- }
-
if (!raw_dump)
printf("\nList of pre-defined events (to be used in -e):\n\n");

if (argc == 0) {
- print_events(NULL, false);
+ print_events(NULL, raw_dump);
return 0;
}

for (i = 0; i < argc; ++i) {
if (strcmp(argv[i], "tracepoint") == 0)
- print_tracepoint_events(NULL, NULL, false);
+ print_tracepoint_events(NULL, NULL, raw_dump);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)
print_symbol_events(NULL, PERF_TYPE_HARDWARE,
- event_symbols_hw, PERF_COUNT_HW_MAX, false);
+ event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
else if (strcmp(argv[i], "sw") == 0 ||
strcmp(argv[i], "software") == 0)
print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
- event_symbols_sw, PERF_COUNT_SW_MAX, false);
+ event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0)
- print_hwcache_events(NULL, false);
+ print_hwcache_events(NULL, raw_dump);
else if (strcmp(argv[i], "pmu") == 0)
- print_pmu_events(NULL, false);
+ print_pmu_events(NULL, raw_dump);
else {
char *sep = strchr(argv[i], ':'), *s;
int sep_idx;

if (sep == NULL) {
- print_events(argv[i], false);
+ print_events(argv[i], raw_dump);
continue;
}
sep_idx = sep - argv[i];
@@ -79,7 +74,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
return -1;

s[sep_idx] = '\0';
- print_tracepoint_events(s, s + sep_idx + 1, false);
+ print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
free(s);
}
}
--
1.8.5.5

2015-02-27 10:19:43

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 5/7] perf list: Avoid confusion of perf output and the next command prompt

Distinguish the output of 'perf list --list-opts' or 'perf --list-cmds'
with the next command prompt, which also happens in other cases (e.g.
record, report ...).

Example:

Before this patch:

$perf list --list-opts
--raw-dump $ <-- the output and the next command prompt are at
the same line

After this patch:

$perf list --list-opts
--raw-dump
$ <-- the new line

Signed-off-by: Yunlong Song <[email protected]>
---
tools/perf/perf.c | 1 +
tools/perf/util/parse-options.c | 2 ++
2 files changed, 3 insertions(+)

diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index f3c66b8..3df2665 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -223,6 +223,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
struct cmd_struct *p = commands+i;
printf("%s ", p->cmd);
}
+ putchar('\n');
exit(0);
} else if (!strcmp(cmd, "--debug")) {
if (*argc < 2) {
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 4ee9a86..b0ef2d8 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -508,12 +508,14 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
printf("--%s ", options->long_name);
options++;
}
+ putchar('\n');
exit(130);
case PARSE_OPT_LIST_SUBCMDS:
if (subcommands) {
for (int i = 0; subcommands[i]; i++)
printf("%s ", subcommands[i]);
}
+ putchar('\n');
exit(130);
default: /* PARSE_OPT_UNKNOWN */
if (ctx.argv[0][1] == '-') {
--
1.8.5.5

2015-02-27 10:19:45

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 6/7] perf record: Remove the '--(null)' long_name for --list-opts

If the long_name of a 'struct option' is defined as NULL, --list-opts
will incorrectly print '--(null)' in its output. As a result, '--(null)'
will finally appear in the case of bash completion, e.g. 'perf record --'.

Example:

Before this patch:

$ perf record --list-opts

--event --filter --pid --tid --realtime --no-buffering --raw-samples
--all-cpus --cpu --count --output --no-inherit --freq --mmap-pages
--group --(null) --call-graph --verbose --quiet --stat --data
--timestamp --period --no-samples --no-buildid-cache --no-buildid
--cgroup --delay --uid --branch-any --branch-filter --weight
--transaction --per-thread --intr-regs

After this patch:

$ perf record --list-opts

--event --filter --pid --tid --realtime --no-buffering --raw-samples
--all-cpus --cpu --count --output --no-inherit --freq --mmap-pages
--group --call-graph --verbose --quiet --stat --data --timestamp
--period --no-samples --no-buildid-cache --no-buildid --cgroup --delay
--uid --branch-any --branch-filter --weight --transaction --per-thread
--intr-regs

Signed-off-by: Yunlong Song <[email protected]>
---
tools/perf/util/parse-options.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index b0ef2d8..1457d66 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -505,7 +505,8 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
break;
case PARSE_OPT_LIST_OPTS:
while (options->type != OPTION_END) {
- printf("--%s ", options->long_name);
+ if (options->long_name)
+ printf("--%s ", options->long_name);
options++;
}
putchar('\n');
--
1.8.5.5

2015-02-27 10:19:46

by Yunlong Song

[permalink] [raw]
Subject: [PATCH v3 7/7] perf core: Fix the bash completion problem of 'perf --*'

The perf-completion.sh uses a predefined string '--help --version
--exec-path --html-path --paginate --no-pager --perf-dir --work-tree
--debugfs-dir' for the bash completion of 'perf --*', which has two
problems:

Problem 1: If the options of perf are changed (see handle_options() in
perf.c), the perf-completion.sh has to be changed at the same time. If
not, the bash completion of 'perf --*' and the options which perf
really supports will be inconsistent.

Problem 2: When typing another single character after 'perf --', e.g.
'h', and hit TAB key to get the bash completion of 'perf --h', the
character 'h' disappears at once. This is not what we want, we wish the
bash completion can return '--help --html-path' and then we can
continue to choose one.

To solve this problem, we add '--list-opts' to perf, which now supports
'perf --list-opts' directly, and its result can be used in bash
completion now.

Example:

Before this patch:

$ perf --h <-- hit TAB key after character 'h'
$ perf -- <-- 'h' disappears and no required result

After this patch:

$ perf --h <-- hit TAB key after character 'h'
--help --html-path <-- the required result

Signed-off-by: Yunlong Song <[email protected]>
---
tools/perf/perf-completion.sh | 6 ++----
tools/perf/perf.c | 27 +++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh
index 3356984..c2595e9 100644
--- a/tools/perf/perf-completion.sh
+++ b/tools/perf/perf-completion.sh
@@ -110,13 +110,11 @@ __perf_main ()
# List perf subcommands or long options
if [ $cword -eq 1 ]; then
if [[ $cur == --* ]]; then
- __perfcomp '--help --version \
- --exec-path --html-path --paginate --no-pager \
- --perf-dir --work-tree --debugfs-dir' -- "$cur"
+ cmds=$($cmd --list-opts)
else
cmds=$($cmd --list-cmds)
- __perfcomp "$cmds" "$cur"
fi
+ __perfcomp "$cmds" "$cur"
# List possible events for -e option
elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
evts=$($cmd list --raw-dump)
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 3df2665..b857fcb 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -13,6 +13,7 @@
#include "util/quote.h"
#include "util/run-command.h"
#include "util/parse-events.h"
+#include "util/parse-options.h"
#include "util/debug.h"
#include <api/fs/debugfs.h>
#include <pthread.h>
@@ -125,6 +126,23 @@ static void commit_pager_choice(void)
}
}

+struct option options[] = {
+ OPT_ARGUMENT("help", "help"),
+ OPT_ARGUMENT("version", "version"),
+ OPT_ARGUMENT("exec-path", "exec-path"),
+ OPT_ARGUMENT("html-path", "html-path"),
+ OPT_ARGUMENT("paginate", "paginate"),
+ OPT_ARGUMENT("no-pager", "no-pager"),
+ OPT_ARGUMENT("perf-dir", "perf-dir"),
+ OPT_ARGUMENT("work-tree", "work-tree"),
+ OPT_ARGUMENT("debugfs-dir", "debugfs-dir"),
+ OPT_ARGUMENT("buildid-dir", "buildid-dir"),
+ OPT_ARGUMENT("list-cmds", "list-cmds"),
+ OPT_ARGUMENT("list-opts", "list-opts"),
+ OPT_ARGUMENT("debug", "debug"),
+ OPT_END()
+};
+
static int handle_options(const char ***argv, int *argc, int *envchanged)
{
int handled = 0;
@@ -225,6 +243,15 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
}
putchar('\n');
exit(0);
+ } else if (!strcmp(cmd, "--list-opts")) {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(options)-1; i++) {
+ struct option *p = options+i;
+ printf("--%s ", p->long_name);
+ }
+ putchar('\n');
+ exit(0);
} else if (!strcmp(cmd, "--debug")) {
if (*argc < 2) {
fprintf(stderr, "No variable specified for --debug.\n");
--
1.8.5.5

2015-02-27 13:57:33

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH v3 1/7] perf list: Sort the output of 'perf list' to view more clearly

Em Fri, Feb 27, 2015 at 06:21:25PM +0800, Yunlong Song escreveu:
> Sort the output according to ASCII character list (using strcmp), which
> supports both number sequence and alphabet sequence.

That is a great feature to have, thanks!

But you are not calling closedir() if zalloc fails, I am fixing that for
you this time, please check those kinds of things in the future.

- Arnaldo
> @@ -1100,11 +1108,21 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
> struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
> char evt_path[MAXPATHLEN];
> char dir_path[MAXPATHLEN];
> + char **evt_list = NULL;
> + unsigned int evt_i = 0, evt_num = 0;
> + bool evt_num_known = false;
>
> +restart:
> sys_dir = opendir(tracing_events_path);
> if (!sys_dir)
> return;
>
> + if (evt_num_known) {
> + evt_list = zalloc(sizeof(char *) * evt_num);
> + if (!evt_list)
> + goto out_enomem;

Here, zalloc fails, goto out_enomem, closedir() not called.

> +out_free:
> + evt_num = evt_i;
> + for (evt_i = 0; evt_i < evt_num; evt_i++)
> + zfree(&evt_list[evt_i]);
> + zfree(&evt_list);
> + return;
> +
> +out_enomem:
> + printf("FATAL: not enough memory to print %s\n",
> + event_type_descriptors[PERF_TYPE_TRACEPOINT]);
> + if (evt_list)
> + goto out_free;
> }

2015-02-27 15:20:52

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH v3 4/7] perf list: Extend raw-dump to certain kind of events

Em Fri, Feb 27, 2015 at 06:21:28PM +0800, Yunlong Song escreveu:
> Extend 'perf list --raw-dump' to 'perf list --raw-dump [hw|sw|cache
> |tracepoint|pmu|event_glob]' in order to show the raw-dump of a
> certain kind of events rather than all of the events.
>
> Example:
>
> Before this patch:
>
> $ perf list --raw-dump hw
> branch-instructions branch-misses bus-cycles cache-misses
> cache-references cpu-cycles instructions stalled-cycles-backend
> stalled-cycles-frontend
> alignment-faults context-switches cpu-clock cpu-migrations
> emulation-faults major-faults minor-faults page-faults task-clock
> ...
> ...
> writeback:writeback_thread_start writeback:writeback_thread_stop
> writeback:writeback_wait_iff_congested
> writeback:writeback_wake_background writeback:writeback_wake_thread
>
> As shown above, all of the events are printed.

This patch doesn't apply, please check later in my perf/core branch to
resolve this issue, ok? I am processing the other patches in this
series.

- Arnaldo

2015-02-27 15:23:56

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH v3 4/7] perf list: Extend raw-dump to certain kind of events

Em Fri, Feb 27, 2015 at 12:20:46PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Fri, Feb 27, 2015 at 06:21:28PM +0800, Yunlong Song escreveu:
> > Extend 'perf list --raw-dump' to 'perf list --raw-dump [hw|sw|cache
> > |tracepoint|pmu|event_glob]' in order to show the raw-dump of a
> > certain kind of events rather than all of the events.
> >
> > Example:
> >
> > Before this patch:
> >
> > $ perf list --raw-dump hw
> > branch-instructions branch-misses bus-cycles cache-misses
> > cache-references cpu-cycles instructions stalled-cycles-backend
> > stalled-cycles-frontend
> > alignment-faults context-switches cpu-clock cpu-migrations
> > emulation-faults major-faults minor-faults page-faults task-clock
> > ...
> > ...
> > writeback:writeback_thread_start writeback:writeback_thread_stop
> > writeback:writeback_wait_iff_congested
> > writeback:writeback_wake_background writeback:writeback_wake_thread
> >
> > As shown above, all of the events are printed.
>
> This patch doesn't apply, please check later in my perf/core branch to
> resolve this issue, ok? I am processing the other patches in this
> series.

My bad, the patches came out of order and I forgot to use threaded view
in mutt, nevermind, applying...

- Arnaldo

2015-02-27 15:27:08

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH v3 0/7] perf core: Make some improvements and fixes

Em Fri, Feb 27, 2015 at 06:21:24PM +0800, Yunlong Song escreveu:
> Hi,
> Found some functions to improve and bugs to fix in perf.
>
> This patchset is based on:
> https://git.kernel.org/cgit/linux/kernel/git/acme/linux.git/ perf/core
>
> For [PATCH v3 4/7] and [PATCH v3 5/7], I have checked that these
> patches do not break shell autocompletion. Besides, for [PATCH v3 4/7],
> --raw-dump without an argument still shows all the events.

Thanks, all applied (and tested).

- Arnaldo

Subject: [tip:perf/core] perf list: Sort the output of 'perf list' to view more clearly

Commit-ID: ab0e48002db818c1937f105cd18001dfdd3ce056
Gitweb: http://git.kernel.org/tip/ab0e48002db818c1937f105cd18001dfdd3ce056
Author: Yunlong Song <[email protected]>
AuthorDate: Fri, 27 Feb 2015 18:21:25 +0800
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Feb 2015 15:51:44 -0300

perf list: Sort the output of 'perf list' to view more clearly

Sort the output according to ASCII character list (using strcmp), which
supports both number sequence and alphabet sequence.

Example:

Before this patch:

$ perf list

List of pre-defined events (to be used in -e):
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
cache-references [Hardware event]
cache-misses [Hardware event]
branch-instructions OR branches [Hardware event]
branch-misses [Hardware event]
bus-cycles [Hardware event]
... ...

jbd2:jbd2_start_commit [Tracepoint event]
jbd2:jbd2_commit_locking [Tracepoint event]
jbd2:jbd2_run_stats [Tracepoint event]
block:block_rq_issue [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_bio_backmerge [Tracepoint event]
block:block_getrq [Tracepoint event]
... ...

After this patch:

$ perf list

List of pre-defined events (to be used in -e):
branch-instructions OR branches [Hardware event]
branch-misses [Hardware event]
bus-cycles [Hardware event]
cache-misses [Hardware event]
cache-references [Hardware event]
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
... ...

block:block_bio_backmerge [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_getrq [Tracepoint event]
block:block_rq_issue [Tracepoint event]
jbd2:jbd2_commit_locking [Tracepoint event]
jbd2:jbd2_run_stats [Tracepoint event]
jbd2:jbd2_start_commit [Tracepoint event]
... ...

Signed-off-by: Yunlong Song <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
[ Don't forget closedir({sys,evt}_dir) when handling errors ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/parse-events.c | 216 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 193 insertions(+), 23 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 109ba5c..f6822d9 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1089,6 +1089,14 @@ static const char * const event_type_descriptors[] = {
"Hardware breakpoint",
};

+static int cmp_string(const void *a, const void *b)
+{
+ const char * const *as = a;
+ const char * const *bs = b;
+
+ return strcmp(*as, *bs);
+}
+
/*
* Print the events from <debugfs_mount_point>/tracing/events
*/
@@ -1100,11 +1108,21 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
+ char **evt_list = NULL;
+ unsigned int evt_i = 0, evt_num = 0;
+ bool evt_num_known = false;

+restart:
sys_dir = opendir(tracing_events_path);
if (!sys_dir)
return;

+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_close_sys_dir;
+ }
+
for_each_subsystem(sys_dir, sys_dirent, sys_next) {
if (subsys_glob != NULL &&
!strglobmatch(sys_dirent.d_name, subsys_glob))
@@ -1121,19 +1139,56 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
!strglobmatch(evt_dirent.d_name, event_glob))
continue;

- if (name_only) {
- printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name);
+ if (!evt_num_known) {
+ evt_num++;
continue;
}

snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name);
- printf(" %-50s [%s]\n", evt_path,
- event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+
+ evt_list[evt_i] = strdup(evt_path);
+ if (evt_list[evt_i] == NULL)
+ goto out_close_evt_dir;
+ evt_i++;
}
closedir(evt_dir);
}
closedir(sys_dir);
+
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
+ }
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num) {
+ if (name_only) {
+ printf("%s ", evt_list[evt_i++]);
+ continue;
+ }
+ printf(" %-50s [%s]\n", evt_list[evt_i++],
+ event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+ }
+ if (evt_num)
+ printf("\n");
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return;
+
+out_close_evt_dir:
+ closedir(evt_dir);
+out_close_sys_dir:
+ closedir(sys_dir);
+
+ printf("FATAL: not enough memory to print %s\n",
+ event_type_descriptors[PERF_TYPE_TRACEPOINT]);
+ if (evt_list)
+ goto out_free;
}

/*
@@ -1218,20 +1273,61 @@ static void __print_events_type(u8 type, struct event_symbol *syms,
unsigned max)
{
char name[64];
- unsigned i;
+ unsigned int i, evt_i = 0, evt_num = 0;
+ char **evt_list = NULL;
+ bool evt_num_known = false;
+
+restart:
+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_enomem;
+ syms -= max;
+ }

for (i = 0; i < max ; i++, syms++) {
if (!is_event_supported(type, i))
continue;

+ if (!evt_num_known) {
+ evt_num++;
+ continue;
+ }
+
if (strlen(syms->alias))
snprintf(name, sizeof(name), "%s OR %s",
syms->symbol, syms->alias);
else
snprintf(name, sizeof(name), "%s", syms->symbol);

- printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
+ evt_list[evt_i] = strdup(name);
+ if (evt_list[evt_i] == NULL)
+ goto out_enomem;
+ evt_i++;
+ }
+
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
}
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num)
+ printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
+ if (evt_num)
+ printf("\n");
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return;
+
+out_enomem:
+ printf("FATAL: not enough memory to print %s\n", event_type_descriptors[type]);
+ if (evt_list)
+ goto out_free;
}

void print_events_type(u8 type)
@@ -1244,8 +1340,17 @@ void print_events_type(u8 type)

int print_hwcache_events(const char *event_glob, bool name_only)
{
- unsigned int type, op, i, printed = 0;
+ unsigned int type, op, i, evt_i = 0, evt_num = 0;
char name[64];
+ char **evt_list = NULL;
+ bool evt_num_known = false;
+
+restart:
+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_enomem;
+ }

for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
@@ -1263,27 +1368,66 @@ int print_hwcache_events(const char *event_glob, bool name_only)
type | (op << 8) | (i << 16)))
continue;

- if (name_only)
- printf("%s ", name);
- else
- printf(" %-50s [%s]\n", name,
- event_type_descriptors[PERF_TYPE_HW_CACHE]);
- ++printed;
+ if (!evt_num_known) {
+ evt_num++;
+ continue;
+ }
+
+ evt_list[evt_i] = strdup(name);
+ if (evt_list[evt_i] == NULL)
+ goto out_enomem;
+ evt_i++;
}
}
}

- if (printed)
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
+ }
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num) {
+ if (name_only) {
+ printf("%s ", evt_list[evt_i++]);
+ continue;
+ }
+ printf(" %-50s [%s]\n", evt_list[evt_i++],
+ event_type_descriptors[PERF_TYPE_HW_CACHE]);
+ }
+ if (evt_num)
printf("\n");
- return printed;
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return evt_num;
+
+out_enomem:
+ printf("FATAL: not enough memory to print %s\n", event_type_descriptors[PERF_TYPE_HW_CACHE]);
+ if (evt_list)
+ goto out_free;
+ return evt_num;
}

static void print_symbol_events(const char *event_glob, unsigned type,
struct event_symbol *syms, unsigned max,
bool name_only)
{
- unsigned i, printed = 0;
+ unsigned int i, evt_i = 0, evt_num = 0;
char name[MAX_NAME_LEN];
+ char **evt_list = NULL;
+ bool evt_num_known = false;
+
+restart:
+ if (evt_num_known) {
+ evt_list = zalloc(sizeof(char *) * evt_num);
+ if (!evt_list)
+ goto out_enomem;
+ syms -= max;
+ }

for (i = 0; i < max; i++, syms++) {

@@ -1295,23 +1439,49 @@ static void print_symbol_events(const char *event_glob, unsigned type,
if (!is_event_supported(type, i))
continue;

- if (name_only) {
- printf("%s ", syms->symbol);
+ if (!evt_num_known) {
+ evt_num++;
continue;
}

- if (strlen(syms->alias))
+ if (!name_only && strlen(syms->alias))
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
else
strncpy(name, syms->symbol, MAX_NAME_LEN);

- printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
-
- printed++;
+ evt_list[evt_i] = strdup(name);
+ if (evt_list[evt_i] == NULL)
+ goto out_enomem;
+ evt_i++;
}

- if (printed)
+ if (!evt_num_known) {
+ evt_num_known = true;
+ goto restart;
+ }
+ qsort(evt_list, evt_num, sizeof(char *), cmp_string);
+ evt_i = 0;
+ while (evt_i < evt_num) {
+ if (name_only) {
+ printf("%s ", evt_list[evt_i++]);
+ continue;
+ }
+ printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
+ }
+ if (evt_num)
printf("\n");
+
+out_free:
+ evt_num = evt_i;
+ for (evt_i = 0; evt_i < evt_num; evt_i++)
+ zfree(&evt_list[evt_i]);
+ zfree(&evt_list);
+ return;
+
+out_enomem:
+ printf("FATAL: not enough memory to print %s\n", event_type_descriptors[type]);
+ if (evt_list)
+ goto out_free;
}

/*

Subject: [tip:perf/core] perf list: Allow listing events with 'tracepoint' prefix

Commit-ID: 161149513b3570ebd7fe14fc2ddc42cb46557e37
Gitweb: http://git.kernel.org/tip/161149513b3570ebd7fe14fc2ddc42cb46557e37
Author: Yunlong Song <[email protected]>
AuthorDate: Fri, 27 Feb 2015 18:21:26 +0800
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Feb 2015 15:51:51 -0300

perf list: Allow listing events with 'tracepoint' prefix

If somebody happens to name an event with the beginning of 'tracepoint'
(e.g. tracepoint_foo), then it will never be showed with perf list
event_glob, thus we parse the argument 'tracepoint' more carefully for
accuracy.

Example:

Before this patch:

$ perf list tracepoint_foo:*

jbd2:jbd2_start_commit [Tracepoint event]
jbd2:jbd2_commit_locking [Tracepoint event]
jbd2:jbd2_run_stats [Tracepoint event]
block:block_rq_issue [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_bio_backmerge [Tracepoint event]
block:block_getrq [Tracepoint event]
... ...

As shown above, all of the tracepoint events are printed. In fact, the
command's real intention is to print the events of tracepoint_foo.

After this patch:

$ perf list tracepoint_foo:*

tracepoint_foo:tp_foo_enter [Tracepoint event]
tracepoint_foo:tp_foo_exit [Tracepoint event]

As shown above, only the events of tracepoint_foo are printed.

Signed-off-by: Yunlong Song <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-list.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index ad8018e..2acbcf0 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -50,9 +50,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
}

for (i = 0; i < argc; ++i) {
- if (i)
- putchar('\n');
- if (strncmp(argv[i], "tracepoint", 10) == 0)
+ if (strcmp(argv[i], "tracepoint") == 0)
print_tracepoint_events(NULL, NULL, false);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)

Subject: [tip:perf/core] perf list: Avoid confusion of perf output and the next command prompt

Commit-ID: ed45752061be11a40f57df4304296147dbda2da9
Gitweb: http://git.kernel.org/tip/ed45752061be11a40f57df4304296147dbda2da9
Author: Yunlong Song <[email protected]>
AuthorDate: Fri, 27 Feb 2015 18:21:29 +0800
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Feb 2015 15:52:09 -0300

perf list: Avoid confusion of perf output and the next command prompt

Distinguish the output of 'perf list --list-opts' or 'perf --list-cmds'
with the next command prompt, which also happens in other cases (e.g.
record, report ...).

Example:

Before this patch:

$perf list --list-opts
--raw-dump $ <-- the output and the next command prompt are at
the same line

After this patch:

$perf list --list-opts
--raw-dump
$ <-- the new line

Signed-off-by: Yunlong Song <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/perf.c | 1 +
tools/perf/util/parse-options.c | 2 ++
2 files changed, 3 insertions(+)

diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index f3c66b8..3df2665 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -223,6 +223,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
struct cmd_struct *p = commands+i;
printf("%s ", p->cmd);
}
+ putchar('\n');
exit(0);
} else if (!strcmp(cmd, "--debug")) {
if (*argc < 2) {
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 4ee9a86..b0ef2d8 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -508,12 +508,14 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
printf("--%s ", options->long_name);
options++;
}
+ putchar('\n');
exit(130);
case PARSE_OPT_LIST_SUBCMDS:
if (subcommands) {
for (int i = 0; subcommands[i]; i++)
printf("%s ", subcommands[i]);
}
+ putchar('\n');
exit(130);
default: /* PARSE_OPT_UNKNOWN */
if (ctx.argv[0][1] == '-') {

Subject: [tip:perf/core] perf tools: Remove the '--(null)' long_name for --list-opts

Commit-ID: 3ef1e65c829c86ffaa94a4ed59fed5da37f9610a
Gitweb: http://git.kernel.org/tip/3ef1e65c829c86ffaa94a4ed59fed5da37f9610a
Author: Yunlong Song <[email protected]>
AuthorDate: Fri, 27 Feb 2015 18:21:30 +0800
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Feb 2015 15:52:14 -0300

perf tools: Remove the '--(null)' long_name for --list-opts

If the long_name of a 'struct option' is defined as NULL, --list-opts
will incorrectly print '--(null)' in its output. As a result, '--(null)'
will finally appear in the case of bash completion, e.g. 'perf record
--'.

Example:

Before this patch:

$ perf record --list-opts

--event --filter --pid --tid --realtime --no-buffering --raw-samples
--all-cpus --cpu --count --output --no-inherit --freq --mmap-pages
--group --(null) --call-graph --verbose --quiet --stat --data
--timestamp --period --no-samples --no-buildid-cache --no-buildid
--cgroup --delay --uid --branch-any --branch-filter --weight
--transaction --per-thread --intr-regs

After this patch:

$ perf record --list-opts

--event --filter --pid --tid --realtime --no-buffering --raw-samples
--all-cpus --cpu --count --output --no-inherit --freq --mmap-pages
--group --call-graph --verbose --quiet --stat --data --timestamp
--period --no-samples --no-buildid-cache --no-buildid --cgroup --delay
--uid --branch-any --branch-filter --weight --transaction --per-thread
--intr-regs

Signed-off-by: Yunlong Song <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/parse-options.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index b0ef2d8..1457d66 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -505,7 +505,8 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
break;
case PARSE_OPT_LIST_OPTS:
while (options->type != OPTION_END) {
- printf("--%s ", options->long_name);
+ if (options->long_name)
+ printf("--%s ", options->long_name);
options++;
}
putchar('\n');

Subject: [tip:perf/core] perf list: Clean up the printing functions of hardware/software events

Commit-ID: 705750f2d6e283ba2856ba8eda60dce2d405b387
Gitweb: http://git.kernel.org/tip/705750f2d6e283ba2856ba8eda60dce2d405b387
Author: Yunlong Song <[email protected]>
AuthorDate: Fri, 27 Feb 2015 18:21:27 +0800
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Feb 2015 15:52:18 -0300

perf list: Clean up the printing functions of hardware/software events

Do not need print_events_type or __print_events_type for listing hw/sw
events, let print_symbol_events do its job instead. Moreover,
print_symbol_events can also handle event_glob and name_only.

Signed-off-by: Yunlong Song <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-list.c | 6 ++--
tools/perf/util/parse-events.c | 80 ++----------------------------------------
tools/perf/util/parse-events.h | 11 +++++-
3 files changed, 17 insertions(+), 80 deletions(-)

diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 2acbcf0..8b323e0 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -54,10 +54,12 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
print_tracepoint_events(NULL, NULL, false);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)
- print_events_type(PERF_TYPE_HARDWARE);
+ print_symbol_events(NULL, PERF_TYPE_HARDWARE,
+ event_symbols_hw, PERF_COUNT_HW_MAX, false);
else if (strcmp(argv[i], "sw") == 0 ||
strcmp(argv[i], "software") == 0)
- print_events_type(PERF_TYPE_SOFTWARE);
+ print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
+ event_symbols_sw, PERF_COUNT_SW_MAX, false);
else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(NULL, false);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index f6822d9..fe07573 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -20,11 +20,6 @@

#define MAX_NAME_LEN 100

-struct event_symbol {
- const char *symbol;
- const char *alias;
-};
-
#ifdef PARSER_DEBUG
extern int parse_events_debug;
#endif
@@ -39,7 +34,7 @@ static struct perf_pmu_event_symbol *perf_pmu_events_list;
*/
static int perf_pmu_events_list_num;

-static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
+struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = {
.symbol = "cpu-cycles",
.alias = "cycles",
@@ -82,7 +77,7 @@ static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
},
};

-static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
+struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
[PERF_COUNT_SW_CPU_CLOCK] = {
.symbol = "cpu-clock",
.alias = "",
@@ -1269,75 +1264,6 @@ static bool is_event_supported(u8 type, unsigned config)
return ret;
}

-static void __print_events_type(u8 type, struct event_symbol *syms,
- unsigned max)
-{
- char name[64];
- unsigned int i, evt_i = 0, evt_num = 0;
- char **evt_list = NULL;
- bool evt_num_known = false;
-
-restart:
- if (evt_num_known) {
- evt_list = zalloc(sizeof(char *) * evt_num);
- if (!evt_list)
- goto out_enomem;
- syms -= max;
- }
-
- for (i = 0; i < max ; i++, syms++) {
- if (!is_event_supported(type, i))
- continue;
-
- if (!evt_num_known) {
- evt_num++;
- continue;
- }
-
- if (strlen(syms->alias))
- snprintf(name, sizeof(name), "%s OR %s",
- syms->symbol, syms->alias);
- else
- snprintf(name, sizeof(name), "%s", syms->symbol);
-
- evt_list[evt_i] = strdup(name);
- if (evt_list[evt_i] == NULL)
- goto out_enomem;
- evt_i++;
- }
-
- if (!evt_num_known) {
- evt_num_known = true;
- goto restart;
- }
- qsort(evt_list, evt_num, sizeof(char *), cmp_string);
- evt_i = 0;
- while (evt_i < evt_num)
- printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
- if (evt_num)
- printf("\n");
-
-out_free:
- evt_num = evt_i;
- for (evt_i = 0; evt_i < evt_num; evt_i++)
- zfree(&evt_list[evt_i]);
- zfree(&evt_list);
- return;
-
-out_enomem:
- printf("FATAL: not enough memory to print %s\n", event_type_descriptors[type]);
- if (evt_list)
- goto out_free;
-}
-
-void print_events_type(u8 type)
-{
- if (type == PERF_TYPE_SOFTWARE)
- __print_events_type(type, event_symbols_sw, PERF_COUNT_SW_MAX);
- else
- __print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX);
-}
-
int print_hwcache_events(const char *event_glob, bool name_only)
{
unsigned int type, op, i, evt_i = 0, evt_num = 0;
@@ -1412,7 +1338,7 @@ out_enomem:
return evt_num;
}

-static void print_symbol_events(const char *event_glob, unsigned type,
+void print_symbol_events(const char *event_glob, unsigned type,
struct event_symbol *syms, unsigned max,
bool name_only)
{
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 39c3b57..52a2dda 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -116,7 +116,16 @@ void parse_events_update_lists(struct list_head *list_event,
void parse_events_error(void *data, void *scanner, char const *msg);

void print_events(const char *event_glob, bool name_only);
-void print_events_type(u8 type);
+
+struct event_symbol {
+ const char *symbol;
+ const char *alias;
+};
+extern struct event_symbol event_symbols_hw[];
+extern struct event_symbol event_symbols_sw[];
+void print_symbol_events(const char *event_glob, unsigned type,
+ struct event_symbol *syms, unsigned max,
+ bool name_only);
void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
bool name_only);
int print_hwcache_events(const char *event_glob, bool name_only);

Subject: [tip:perf/core] perf list: Extend raw-dump to certain kind of events

Commit-ID: 5ef803ee02d67ad0b49f357cb7feb7d5e6b0015d
Gitweb: http://git.kernel.org/tip/5ef803ee02d67ad0b49f357cb7feb7d5e6b0015d
Author: Yunlong Song <[email protected]>
AuthorDate: Fri, 27 Feb 2015 18:21:28 +0800
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Feb 2015 15:52:24 -0300

perf list: Extend raw-dump to certain kind of events

Extend 'perf list --raw-dump' to 'perf list --raw-dump [hw|sw|cache
|tracepoint|pmu|event_glob]' in order to show the raw-dump of a certain
kind of events rather than all of the events.

Example:

Before this patch:

$ perf list --raw-dump hw
branch-instructions branch-misses bus-cycles cache-misses
cache-references cpu-cycles instructions stalled-cycles-backend
stalled-cycles-frontend
alignment-faults context-switches cpu-clock cpu-migrations
emulation-faults major-faults minor-faults page-faults task-clock
...
...
writeback:writeback_thread_start writeback:writeback_thread_stop
writeback:writeback_wait_iff_congested
writeback:writeback_wake_background writeback:writeback_wake_thread

As shown above, all of the events are printed.

After this patch:

$ perf list --raw-dump hw
branch-instructions branch-misses bus-cycles cache-misses
cache-references cpu-cycles instructions stalled-cycles-backend
stalled-cycles-frontend

As shown above, only the hw events are printed.

Signed-off-by: Yunlong Song <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/Documentation/perf-list.txt | 6 ++++++
tools/perf/builtin-list.c | 21 ++++++++-------------
2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index 3e2aec9..4692d27 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -127,6 +127,12 @@ To limit the list use:
One or more types can be used at the same time, listing the events for the
types specified.

+Support raw format:
+
+. '--raw-dump', shows the raw-dump of all the events.
+. '--raw-dump [hw|sw|cache|tracepoint|pmu|event_glob]', shows the raw-dump of
+ a certain kind of events.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-top[1],
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 8b323e0..af5bd05 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -36,41 +36,36 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)

setup_pager();

- if (raw_dump) {
- print_events(NULL, true);
- return 0;
- }
-
if (!raw_dump)
printf("\nList of pre-defined events (to be used in -e):\n\n");

if (argc == 0) {
- print_events(NULL, false);
+ print_events(NULL, raw_dump);
return 0;
}

for (i = 0; i < argc; ++i) {
if (strcmp(argv[i], "tracepoint") == 0)
- print_tracepoint_events(NULL, NULL, false);
+ print_tracepoint_events(NULL, NULL, raw_dump);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)
print_symbol_events(NULL, PERF_TYPE_HARDWARE,
- event_symbols_hw, PERF_COUNT_HW_MAX, false);
+ event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
else if (strcmp(argv[i], "sw") == 0 ||
strcmp(argv[i], "software") == 0)
print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
- event_symbols_sw, PERF_COUNT_SW_MAX, false);
+ event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0)
- print_hwcache_events(NULL, false);
+ print_hwcache_events(NULL, raw_dump);
else if (strcmp(argv[i], "pmu") == 0)
- print_pmu_events(NULL, false);
+ print_pmu_events(NULL, raw_dump);
else {
char *sep = strchr(argv[i], ':'), *s;
int sep_idx;

if (sep == NULL) {
- print_events(argv[i], false);
+ print_events(argv[i], raw_dump);
continue;
}
sep_idx = sep - argv[i];
@@ -79,7 +74,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
return -1;

s[sep_idx] = '\0';
- print_tracepoint_events(s, s + sep_idx + 1, false);
+ print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
free(s);
}
}

Subject: [tip:perf/core] perf tools: Fix the bash completion problem of ' perf --*'

Commit-ID: 7335399a6a4bead9ef8b59ce7d811fc4e99ca98c
Gitweb: http://git.kernel.org/tip/7335399a6a4bead9ef8b59ce7d811fc4e99ca98c
Author: Yunlong Song <[email protected]>
AuthorDate: Fri, 27 Feb 2015 18:21:31 +0800
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Feb 2015 15:52:28 -0300

perf tools: Fix the bash completion problem of 'perf --*'

The perf-completion.sh uses a predefined string '--help --version
--exec-path --html-path --paginate --no-pager --perf-dir --work-tree
--debugfs-dir' for the bash completion of 'perf --*', which has two
problems:

Problem 1: If the options of perf are changed (see handle_options() in
perf.c), the perf-completion.sh has to be changed at the same time. If
not, the bash completion of 'perf --*' and the options which perf
really supports will be inconsistent.

Problem 2: When typing another single character after 'perf --', e.g.
'h', and hit TAB key to get the bash completion of 'perf --h', the
character 'h' disappears at once. This is not what we want, we wish the
bash completion can return '--help --html-path' and then we can
continue to choose one.

To solve this problem, we add '--list-opts' to perf, which now supports
'perf --list-opts' directly, and its result can be used in bash
completion now.

Example:

Before this patch:

$ perf --h <-- hit TAB key after character 'h'
$ perf -- <-- 'h' disappears and no required result

After this patch:

$ perf --h <-- hit TAB key after character 'h'
--help --html-path <-- the required result

Signed-off-by: Yunlong Song <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/perf-completion.sh | 6 ++----
tools/perf/perf.c | 27 +++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh
index 3356984..c2595e9 100644
--- a/tools/perf/perf-completion.sh
+++ b/tools/perf/perf-completion.sh
@@ -110,13 +110,11 @@ __perf_main ()
# List perf subcommands or long options
if [ $cword -eq 1 ]; then
if [[ $cur == --* ]]; then
- __perfcomp '--help --version \
- --exec-path --html-path --paginate --no-pager \
- --perf-dir --work-tree --debugfs-dir' -- "$cur"
+ cmds=$($cmd --list-opts)
else
cmds=$($cmd --list-cmds)
- __perfcomp "$cmds" "$cur"
fi
+ __perfcomp "$cmds" "$cur"
# List possible events for -e option
elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
evts=$($cmd list --raw-dump)
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 3df2665..b857fcb 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -13,6 +13,7 @@
#include "util/quote.h"
#include "util/run-command.h"
#include "util/parse-events.h"
+#include "util/parse-options.h"
#include "util/debug.h"
#include <api/fs/debugfs.h>
#include <pthread.h>
@@ -125,6 +126,23 @@ static void commit_pager_choice(void)
}
}

+struct option options[] = {
+ OPT_ARGUMENT("help", "help"),
+ OPT_ARGUMENT("version", "version"),
+ OPT_ARGUMENT("exec-path", "exec-path"),
+ OPT_ARGUMENT("html-path", "html-path"),
+ OPT_ARGUMENT("paginate", "paginate"),
+ OPT_ARGUMENT("no-pager", "no-pager"),
+ OPT_ARGUMENT("perf-dir", "perf-dir"),
+ OPT_ARGUMENT("work-tree", "work-tree"),
+ OPT_ARGUMENT("debugfs-dir", "debugfs-dir"),
+ OPT_ARGUMENT("buildid-dir", "buildid-dir"),
+ OPT_ARGUMENT("list-cmds", "list-cmds"),
+ OPT_ARGUMENT("list-opts", "list-opts"),
+ OPT_ARGUMENT("debug", "debug"),
+ OPT_END()
+};
+
static int handle_options(const char ***argv, int *argc, int *envchanged)
{
int handled = 0;
@@ -225,6 +243,15 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
}
putchar('\n');
exit(0);
+ } else if (!strcmp(cmd, "--list-opts")) {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(options)-1; i++) {
+ struct option *p = options+i;
+ printf("--%s ", p->long_name);
+ }
+ putchar('\n');
+ exit(0);
} else if (!strcmp(cmd, "--debug")) {
if (*argc < 2) {
fprintf(stderr, "No variable specified for --debug.\n");