2015-06-02 17:13:05

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH V13 00/14] perf, tools: Add support for PMU events in JSON format

CPUs support a large number of performance monitoring events (PMU events)
and often these events are very specific to an architecture/model of the
CPU. To use most of these PMU events with perf, we currently have to identify
them by their raw codes:

perf stat -e r100f2 sleep 1

This patchset allows architectures to specify these PMU events in JSON
files located in 'tools/perf/pmu-events/arch/' of the mainline tree.
The events from the JSON files for the architecture are then built into
the perf binary.

At run time, perf identifies the specific set of events for the CPU and
creates "event aliases". These aliases allow users to specify events by
"name" as:

perf stat -e pm_1plus_ppc_cmpl sleep 1

The file, 'tools/perf/pmu-events/README' in [PATCH 14/14] gives more
details.

Note:
- All known events tables for the architecture are included in the
perf binary.

- For architectures that don't have any JSON files, an empty mapping
table is created and they should continue to build)

Thanks to input from Andi Kleen, Jiri Olsa, Namhyung Kim and Ingo Molnar.

These patches are available from:

https://github.com:sukadev/linux.git

Branch Description
------------------------------------------------------
json-v13 Source Code only
json-files-3 x86 and Powerpc datafiles only
json-v13-with-data Both code and data (build/test)

NOTE: Only "source code" patches (i.e those in json-v13) are being emailed.
Please pull the "data files" from the json-files-3 branch.

Changelog[v13]

Version: Individual patches have their own history :-) that I am
preserving. Patchset version (v13) is for overall patchset and is
somewhat arbitrary.

- Added support for "categories" of events to perf
- Add mapfile, jevents build dependency on pmu-events.c
- Silence jevents when parsing JSON files unless V=1 is specified
- Cleanup error messages
- Fix memory leak with ->cpuid
- Rebase to Arnaldo's tree
- Allow overriding CPUID via environment variable
- Support long descriptions for events
- Handle header line in mapfile.csv
- Cleanup JSON files (trim PublicDescription if identical to/prefix of
BriefDescription field)


Andi Kleen (12):
perf, tools: Add jsmn `jasmine' JSON parser
perf, tools, jevents: Program to convert JSON file to C style file
perf, tools: Allow events with dot
perf, tools: Support CPU id matching for x86 v2
perf, tools: Support alias descriptions
perf, tools: Query terminal width and use in perf list
perf, tools: Add a --no-desc flag to perf list
perf, tools: Group alias perf list by section
perf, tools: Add override support for event list CPUID
perf, tools: Support long descriptions with perf list -v
perf, tools: Add support for event list topics
perf, tools: Handle header line in mapfile

Sukadev Bhattiprolu (1):
perf, tools: Use pmu_events_map table to create event aliases

tools/perf/Documentation/perf-list.txt | 8 +-
tools/perf/Makefile.perf | 25 +-
tools/perf/arch/powerpc/util/header.c | 11 +
tools/perf/arch/x86/util/header.c | 24 +-
tools/perf/builtin-list.c | 15 +-
tools/perf/pmu-events/Build | 11 +
tools/perf/pmu-events/README | 67 +++
tools/perf/pmu-events/jevents.c | 714 ++++++++++++++++++++++++++++++++
tools/perf/pmu-events/jevents.h | 18 +
tools/perf/pmu-events/jsmn.c | 313 ++++++++++++++
tools/perf/pmu-events/jsmn.h | 67 +++
tools/perf/pmu-events/json.c | 162 ++++++++
tools/perf/pmu-events/json.h | 39 ++
tools/perf/pmu-events/pmu-events.h | 37 ++
tools/perf/util/cache.h | 1 +
tools/perf/util/header.h | 3 +-
tools/perf/util/pager.c | 15 +
tools/perf/util/parse-events.c | 4 +-
tools/perf/util/parse-events.h | 2 +-
tools/perf/util/parse-events.l | 5 +-
tools/perf/util/pmu.c | 230 ++++++++--
tools/perf/util/pmu.h | 6 +-
22 files changed, 1725 insertions(+), 52 deletions(-)
create mode 100644 tools/perf/pmu-events/Build
create mode 100644 tools/perf/pmu-events/README
create mode 100644 tools/perf/pmu-events/jevents.c
create mode 100644 tools/perf/pmu-events/jevents.h
create mode 100644 tools/perf/pmu-events/jsmn.c
create mode 100644 tools/perf/pmu-events/jsmn.h
create mode 100644 tools/perf/pmu-events/json.c
create mode 100644 tools/perf/pmu-events/json.h
create mode 100644 tools/perf/pmu-events/pmu-events.h

--
1.7.9.5


2015-06-02 17:13:16

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 01/14] perf, tools: Add jsmn `jasmine' JSON parser

From: Andi Kleen <[email protected]>

I need a JSON parser. This adds the simplest JSON
parser I could find -- Serge Zaitsev's jsmn `jasmine' --
to the perf library. I merely converted it to (mostly)
Linux style and added support for non 0 terminated input.

The parser is quite straight forward and does not
copy any data, just returns tokens with offsets
into the input buffer. So it's relatively efficient
and simple to use.

The code is not fully checkpatch clean, but I didn't
want to completely fork the upstream code.

Original source: http://zserge.bitbucket.org/jsmn.html

In addition I added a simple wrapper that mmaps a json
file and provides some straight forward access functions.

Used in follow-on patches to parse event files.

Acked-by: Namhyung Kim <[email protected]>
Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>
---

v2: Address review feedback.
v3: Minor checkpatch fixes.
v4 (by Sukadev Bhattiprolu)
- Rebase to 4.0 and fix minor conflicts in tools/perf/Makefile.perf
- Report error if specified events file is invalid.
v5 (Sukadev Bhattiprolu)
- Move files to tools/perf/pmu-events/ since parsing of JSON file
now occurs when _building_ rather than running perf.
---
tools/perf/pmu-events/jsmn.c | 313 ++++++++++++++++++++++++++++++++++++++++++
tools/perf/pmu-events/jsmn.h | 67 +++++++++
tools/perf/pmu-events/json.c | 162 ++++++++++++++++++++++
tools/perf/pmu-events/json.h | 36 +++++
4 files changed, 578 insertions(+)
create mode 100644 tools/perf/pmu-events/jsmn.c
create mode 100644 tools/perf/pmu-events/jsmn.h
create mode 100644 tools/perf/pmu-events/json.c
create mode 100644 tools/perf/pmu-events/json.h

diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c
new file mode 100644
index 0000000..11d1fa1
--- /dev/null
+++ b/tools/perf/pmu-events/jsmn.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2010 Serge A. Zaitsev
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Slightly modified by AK to not assume 0 terminated input.
+ */
+
+#include <stdlib.h>
+#include "jsmn.h"
+
+/*
+ * Allocates a fresh unused token from the token pool.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+ jsmntok_t *tokens, size_t num_tokens)
+{
+ jsmntok_t *tok;
+
+ if ((unsigned)parser->toknext >= num_tokens)
+ return NULL;
+ tok = &tokens[parser->toknext++];
+ tok->start = tok->end = -1;
+ tok->size = 0;
+ return tok;
+}
+
+/*
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+ int start, int end)
+{
+ token->type = type;
+ token->start = start;
+ token->end = end;
+ token->size = 0;
+}
+
+/*
+ * Fills next available token with JSON primitive.
+ */
+static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+ size_t len,
+ jsmntok_t *tokens, size_t num_tokens)
+{
+ jsmntok_t *token;
+ int start;
+
+ start = parser->pos;
+
+ for (; parser->pos < len; parser->pos++) {
+ switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+ /*
+ * In strict mode primitive must be followed by ","
+ * or "}" or "]"
+ */
+ case ':':
+#endif
+ case '\t':
+ case '\r':
+ case '\n':
+ case ' ':
+ case ',':
+ case ']':
+ case '}':
+ goto found;
+ default:
+ break;
+ }
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#ifdef JSMN_STRICT
+ /*
+ * In strict mode primitive must be followed by a
+ * comma/object/array.
+ */
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+#endif
+
+found:
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+ parser->pos--; /* parent sees closing brackets */
+ return JSMN_SUCCESS;
+}
+
+/*
+ * Fills next token with JSON string.
+ */
+static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
+ size_t len,
+ jsmntok_t *tokens, size_t num_tokens)
+{
+ jsmntok_t *token;
+ int start = parser->pos;
+
+ /* Skip starting quote */
+ parser->pos++;
+
+ for (; parser->pos < len; parser->pos++) {
+ char c = js[parser->pos];
+
+ /* Quote: end of string */
+ if (c == '\"') {
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_STRING, start+1,
+ parser->pos);
+ return JSMN_SUCCESS;
+ }
+
+ /* Backslash: Quoted symbol expected */
+ if (c == '\\') {
+ parser->pos++;
+ switch (js[parser->pos]) {
+ /* Allowed escaped symbols */
+ case '\"':
+ case '/':
+ case '\\':
+ case 'b':
+ case 'f':
+ case 'r':
+ case 'n':
+ case 't':
+ break;
+ /* Allows escaped symbol \uXXXX */
+ case 'u':
+ /* TODO */
+ break;
+ /* Unexpected symbol */
+ default:
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+ }
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+}
+
+/*
+ * Parse JSON string and fill tokens.
+ */
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+ jsmntok_t *tokens, unsigned int num_tokens)
+{
+ jsmnerr_t r;
+ int i;
+ jsmntok_t *token;
+
+ for (; parser->pos < len; parser->pos++) {
+ char c;
+ jsmntype_t type;
+
+ c = js[parser->pos];
+ switch (c) {
+ case '{':
+ case '[':
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL)
+ return JSMN_ERROR_NOMEM;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+ token->start = parser->pos;
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case '}':
+ case ']':
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type)
+ return JSMN_ERROR_INVAL;
+ parser->toksuper = -1;
+ token->end = parser->pos + 1;
+ break;
+ }
+ }
+ /* Error if unmatched closing bracket */
+ if (i == -1)
+ return JSMN_ERROR_INVAL;
+ for (; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+ break;
+ case '\"':
+ r = jsmn_parse_string(parser, js, len, tokens,
+ num_tokens);
+ if (r < 0)
+ return r;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ break;
+ case '\t':
+ case '\r':
+ case '\n':
+ case ':':
+ case ',':
+ case ' ':
+ break;
+#ifdef JSMN_STRICT
+ /*
+ * In strict mode primitives are:
+ * numbers and booleans.
+ */
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 't':
+ case 'f':
+ case 'n':
+#else
+ /*
+ * In non-strict mode every unquoted value
+ * is a primitive.
+ */
+ /*FALL THROUGH */
+ default:
+#endif
+ r = jsmn_parse_primitive(parser, js, len, tokens,
+ num_tokens);
+ if (r < 0)
+ return r;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ break;
+
+#ifdef JSMN_STRICT
+ /* Unexpected char in strict mode */
+ default:
+ return JSMN_ERROR_INVAL;
+#endif
+ }
+ }
+
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ /* Unmatched opened object or array */
+ if (tokens[i].start != -1 && tokens[i].end == -1)
+ return JSMN_ERROR_PART;
+ }
+
+ return JSMN_SUCCESS;
+}
+
+/*
+ * Creates a new parser based over a given buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser)
+{
+ parser->pos = 0;
+ parser->toknext = 0;
+ parser->toksuper = -1;
+}
+
+const char *jsmn_strerror(jsmnerr_t err)
+{
+ switch (err) {
+ case JSMN_ERROR_NOMEM:
+ return "No enough tokens";
+ case JSMN_ERROR_INVAL:
+ return "Invalid character inside JSON string";
+ case JSMN_ERROR_PART:
+ return "The string is not a full JSON packet, more bytes expected";
+ case JSMN_SUCCESS:
+ return "Success";
+ default:
+ return "Unknown json error";
+ }
+}
diff --git a/tools/perf/pmu-events/jsmn.h b/tools/perf/pmu-events/jsmn.h
new file mode 100644
index 0000000..d666b10
--- /dev/null
+++ b/tools/perf/pmu-events/jsmn.h
@@ -0,0 +1,67 @@
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+/*
+ * JSON type identifier. Basic types are:
+ * o Object
+ * o Array
+ * o String
+ * o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+ JSMN_PRIMITIVE = 0,
+ JSMN_OBJECT = 1,
+ JSMN_ARRAY = 2,
+ JSMN_STRING = 3
+} jsmntype_t;
+
+typedef enum {
+ /* Not enough tokens were provided */
+ JSMN_ERROR_NOMEM = -1,
+ /* Invalid character inside JSON string */
+ JSMN_ERROR_INVAL = -2,
+ /* The string is not a full JSON packet, more bytes expected */
+ JSMN_ERROR_PART = -3,
+ /* Everything was fine */
+ JSMN_SUCCESS = 0
+} jsmnerr_t;
+
+/*
+ * JSON token description.
+ * @param type type (object, array, string etc.)
+ * @param start start position in JSON data string
+ * @param end end position in JSON data string
+ */
+typedef struct {
+ jsmntype_t type;
+ int start;
+ int end;
+ int size;
+} jsmntok_t;
+
+/*
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+ unsigned int pos; /* offset in the JSON string */
+ int toknext; /* next token to allocate */
+ int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+
+/*
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser *parser);
+
+/*
+ * Run JSON parser. It parses a JSON data string into and array of tokens,
+ * each describing a single JSON object.
+ */
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,
+ size_t len,
+ jsmntok_t *tokens, unsigned int num_tokens);
+
+const char *jsmn_strerror(jsmnerr_t err);
+
+#endif /* __JSMN_H_ */
diff --git a/tools/perf/pmu-events/json.c b/tools/perf/pmu-events/json.c
new file mode 100644
index 0000000..87f0c4b
--- /dev/null
+++ b/tools/perf/pmu-events/json.c
@@ -0,0 +1,162 @@
+/* Parse JSON files using the JSMN parser. */
+
+/*
+ * Copyright (c) 2014, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include "jsmn.h"
+#include "json.h"
+#include <linux/kernel.h>
+
+
+static char *mapfile(const char *fn, size_t *size)
+{
+ unsigned ps = sysconf(_SC_PAGESIZE);
+ struct stat st;
+ char *map = NULL;
+ int err;
+ int fd = open(fn, O_RDONLY);
+
+ if (fd < 0 && verbose && fn) {
+ pr_err("Error opening events file '%s': %s\n", fn,
+ strerror(errno));
+ }
+
+ if (fd < 0)
+ return NULL;
+ err = fstat(fd, &st);
+ if (err < 0)
+ goto out;
+ *size = st.st_size;
+ map = mmap(NULL,
+ (st.st_size + ps - 1) & ~(ps - 1),
+ PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED)
+ map = NULL;
+out:
+ close(fd);
+ return map;
+}
+
+static void unmapfile(char *map, size_t size)
+{
+ unsigned ps = sysconf(_SC_PAGESIZE);
+ munmap(map, roundup(size, ps));
+}
+
+/*
+ * Parse json file using jsmn. Return array of tokens,
+ * and mapped file. Caller needs to free array.
+ */
+jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len)
+{
+ jsmn_parser parser;
+ jsmntok_t *tokens;
+ jsmnerr_t res;
+ unsigned sz;
+
+ *map = mapfile(fn, size);
+ if (!*map)
+ return NULL;
+ /* Heuristic */
+ sz = *size * 16;
+ tokens = malloc(sz);
+ if (!tokens)
+ goto error;
+ jsmn_init(&parser);
+ res = jsmn_parse(&parser, *map, *size, tokens,
+ sz / sizeof(jsmntok_t));
+ if (res != JSMN_SUCCESS) {
+ pr_err("%s: json error %s\n", fn, jsmn_strerror(res));
+ goto error_free;
+ }
+ if (len)
+ *len = parser.toknext;
+ return tokens;
+error_free:
+ free(tokens);
+error:
+ unmapfile(*map, *size);
+ return NULL;
+}
+
+void free_json(char *map, size_t size, jsmntok_t *tokens)
+{
+ free(tokens);
+ unmapfile(map, size);
+}
+
+static int countchar(char *map, char c, int end)
+{
+ int i;
+ int count = 0;
+ for (i = 0; i < end; i++)
+ if (map[i] == c)
+ count++;
+ return count;
+}
+
+/* Return line number of a jsmn token */
+int json_line(char *map, jsmntok_t *t)
+{
+ return countchar(map, '\n', t->start) + 1;
+}
+
+static const char * const jsmn_types[] = {
+ [JSMN_PRIMITIVE] = "primitive",
+ [JSMN_ARRAY] = "array",
+ [JSMN_OBJECT] = "object",
+ [JSMN_STRING] = "string"
+};
+
+#define LOOKUP(a, i) ((i) < (sizeof(a)/sizeof(*(a))) ? ((a)[i]) : "?")
+
+/* Return type name of a jsmn token */
+const char *json_name(jsmntok_t *t)
+{
+ return LOOKUP(jsmn_types, t->type);
+}
+
+int json_len(jsmntok_t *t)
+{
+ return t->end - t->start;
+}
+
+/* Is string t equal to s? */
+int json_streq(char *map, jsmntok_t *t, const char *s)
+{
+ unsigned len = json_len(t);
+ return len == strlen(s) && !strncasecmp(map + t->start, s, len);
+}
diff --git a/tools/perf/pmu-events/json.h b/tools/perf/pmu-events/json.h
new file mode 100644
index 0000000..6b8337e
--- /dev/null
+++ b/tools/perf/pmu-events/json.h
@@ -0,0 +1,36 @@
+#ifndef JSON_H
+#define JSON_H 1
+
+#include "jsmn.h"
+
+jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len);
+void free_json(char *map, size_t size, jsmntok_t *tokens);
+int json_line(char *map, jsmntok_t *t);
+const char *json_name(jsmntok_t *t);
+int json_streq(char *map, jsmntok_t *t, const char *s);
+int json_len(jsmntok_t *t);
+
+extern int verbose;
+
+typedef unsigned int bool;
+
+#ifndef true
+#define true 1
+#endif
+
+extern int eprintf(int level, int var, const char *fmt, ...);
+#define pr_fmt(fmt) fmt
+
+#define pr_err(fmt, ...) \
+ eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
+
+#ifndef roundup
+#define roundup(x, y) ( \
+{ \
+ const typeof(y) __y = y; \
+ (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+#endif
+
+#endif
--
1.7.9.5

2015-06-02 17:13:24

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 02/14] perf, tools, jevents: Program to convert JSON file to C style file

From: Andi Kleen <[email protected]>

This is a modified version of an earlier patch by Andi Kleen.

We expect architectures to describe the performance monitoring events
for each CPU in a corresponding JSON file, which look like:

[
{
"EventCode": "0x00",
"UMask": "0x01",
"EventName": "INST_RETIRED.ANY",
"BriefDescription": "Instructions retired from execution.",
"PublicDescription": "Instructions retired from execution.",
"Counter": "Fixed counter 1",
"CounterHTOff": "Fixed counter 1",
"SampleAfterValue": "2000003",
"SampleAfterValue": "2000003",
"MSRIndex": "0",
"MSRValue": "0",
"TakenAlone": "0",
"CounterMask": "0",
"Invert": "0",
"AnyThread": "0",
"EdgeDetect": "0",
"PEBS": "0",
"PRECISE_STORE": "0",
"Errata": "null",
"Offcore": "0"
}
]

We also expect the architectures to provide a mapping between individual
CPUs to their JSON files. Eg:

GenuineIntel-6-1E,V1,/NHM-EP/NehalemEP_core_V1.json,core

which maps each CPU, identified by [vendor, family, model, version, type]
to a JSON file.

Given these files, the program, jevents::
- locates all JSON files for the architecture,
- parses each JSON file and generates a C-style "PMU-events table"
(pmu-events.c)
- locates a mapfile for the architecture
- builds a global table, mapping each model of CPU to the
corresponding PMU-events table.

The 'pmu-events.c' is generated when building perf and added to libperf.a.
The global table pmu_events_map[] table in this pmu-events.c will be used
in perf in a follow-on patch.

If the architecture does not have any JSON files or there is an error in
processing them, an empty mapping file is created. This would allow the
build of perf to proceed even if we are not able to provide aliases for
events.

The parser for JSON files allows parsing Intel style JSON event files. This
allows to use an Intel event list directly with perf. The Intel event lists
can be quite large and are too big to store in unswappable kernel memory.

The conversion from JSON to C-style is straight forward. The parser knows
(very little) Intel specific information, and can be easily extended to
handle fields for other CPUs.

The parser code is partially shared with an independent parsing library,
which is 2-clause BSD licenced. To avoid any conflicts I marked those
files as BSD licenced too. As part of perf they become GPLv2.

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

v2: Address review feedback. Rename option to --event-files
v3: Add JSON example
v4: Update manpages.
v5: Don't remove dot in fixname. Fix compile error. Add include
protection. Comment realloc.
v6: Include debug/util.h
v7: (Sukadev Bhattiprolu)
Rebase to 4.0 and fix some conflicts.
v8: (Sukadev Bhattiprolu)
Move jevents.[hc] to tools/perf/pmu-events/
Rewrite to locate and process arch specific JSON and "map" files;
and generate a C file.
(Removed acked-by Namhyung Kim due to modest changes to patch)
Compile the generated pmu-events.c and add the pmu-events.o to
libperf.a
v9: [Sukadev Bhattiprolu/Andi Kleen] Rename ->vfm to ->cpuid and use
that field to encode the PVR in Power.
Allow blank lines in mapfile.
[Jiri Olsa] Pass ARCH as a parameter to jevents so we don't have
to detect it.
[Jiri Olsa] Use the infrastrastructure to build pmu-events/perf
(Makefile changes from Jiri included in this patch).
[Jiri Olsa, Andi Kleen] Detect changes to JSON files and rebuild
pmu-events.o only if necessary.

v11: - [Andi Kleen] Add mapfile, jevents dependency on pmu-events.c
- [Jiri Olsa] Be silient if arch doesn't have JSON files
- Also silence 'jevents' when parsing JSON files unless V=1 is
specified during build. Cleanup error messages.
---
tools/perf/Makefile.perf | 25 +-
tools/perf/pmu-events/Build | 11 +
tools/perf/pmu-events/jevents.c | 692 ++++++++++++++++++++++++++++++++++++
tools/perf/pmu-events/jevents.h | 17 +
tools/perf/pmu-events/json.h | 3 +
tools/perf/pmu-events/pmu-events.h | 35 ++
6 files changed, 779 insertions(+), 4 deletions(-)
create mode 100644 tools/perf/pmu-events/Build
create mode 100644 tools/perf/pmu-events/jevents.c
create mode 100644 tools/perf/pmu-events/jevents.h
create mode 100644 tools/perf/pmu-events/pmu-events.h

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 5816a3b..6a50fc1 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -272,14 +272,29 @@ strip: $(PROGRAMS) $(OUTPUT)perf

PERF_IN := $(OUTPUT)perf-in.o

+JEVENTS := $(OUTPUT)pmu-events/jevents
+JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o
+PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o
+
+export JEVENTS
+
export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX
build := -f $(srctree)/tools/build/Makefile.build dir=. obj

$(PERF_IN): $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h FORCE
$(Q)$(MAKE) $(build)=perf

-$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN)
- $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(PERF_IN) $(LIBS) -o $@
+$(JEVENTS_IN): FORCE
+ $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=jevents
+
+$(JEVENTS): $(JEVENTS_IN)
+ $(QUIET_LINK)$(CC) $(JEVENTS_IN) -o $@
+
+$(PMU_EVENTS_IN): $(JEVENTS) FORCE
+ $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events
+
+$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@

$(GTK_IN): FORCE
$(Q)$(MAKE) $(build)=gtk
@@ -308,6 +323,8 @@ perf.spec $(SCRIPTS) \
ifneq ($(OUTPUT),)
%.o: $(OUTPUT)%.o
@echo " # Redirected target $@ => $(OUTPUT)$@"
+pmu-events/%.o: $(OUTPUT)pmu-events/%.o
+ @echo " # Redirected target $@ => $(OUTPUT)$@"
util/%.o: $(OUTPUT)util/%.o
@echo " # Redirected target $@ => $(OUTPUT)$@"
bench/%.o: $(OUTPUT)bench/%.o
@@ -535,8 +552,8 @@ clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)$(RM) .config-detected
- $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
- $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
+ $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)pmu-events/jevents $(srctree)/tools/perf/pmu-events/pmu-events.c
+ $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* $(OUTPUT)pmu-events/pmu-events.c
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
$(python-clean)

diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
new file mode 100644
index 0000000..048fa34
--- /dev/null
+++ b/tools/perf/pmu-events/Build
@@ -0,0 +1,11 @@
+jevents-y += json.o jsmn.o jevents.o
+pmu-events-y += pmu-events.o
+JDIR = pmu-events/arch/$(ARCH)
+JSON = $(shell [ -d $(JDIR) ] && \
+ find $(JDIR) -name '*.json' -o -name 'mapfile*')
+#
+# Locate/process JSON files in pmu-events/arch/
+# directory and create tables in pmu-events.c.
+#
+$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JEVENTS)
+ $(Q)$(call echo-cmd,gen)$(JEVENTS) $(ARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V)
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
new file mode 100644
index 0000000..ab83d85
--- /dev/null
+++ b/tools/perf/pmu-events/jevents.c
@@ -0,0 +1,692 @@
+#define _XOPEN_SOURCE 500 /* needed for nftw() */
+
+/* Parse event JSON files */
+
+/*
+ * Copyright (c) 2014, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <sys/time.h> /* getrlimit */
+#include <sys/resource.h> /* getrlimit */
+#include <ftw.h>
+#include <sys/stat.h>
+#include "jsmn.h"
+#include "json.h"
+#include "jevents.h"
+
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((unused))
+#endif
+
+int verbose = 0;
+char *prog;
+
+int eprintf(int level, int var, const char *fmt, ...)
+{
+
+ int ret;
+ va_list args;
+
+ if (var < level)
+ return 0;
+
+ va_start(args, fmt);
+
+ ret = vfprintf(stderr, fmt, args);
+
+ va_end(args);
+
+ return ret;
+}
+
+__attribute__((weak)) char *get_cpu_str(void)
+{
+ return NULL;
+}
+
+static void addfield(char *map, char **dst, const char *sep,
+ const char *a, jsmntok_t *bt)
+{
+ unsigned len = strlen(a) + 1 + strlen(sep);
+ int olen = *dst ? strlen(*dst) : 0;
+ int blen = bt ? json_len(bt) : 0;
+ char *out;
+
+ out = realloc(*dst, len + olen + blen);
+ if (!out) {
+ /* Don't add field in this case */
+ return;
+ }
+ *dst = out;
+
+ if (!olen)
+ *(*dst) = 0;
+ else
+ strcat(*dst, sep);
+ strcat(*dst, a);
+ if (bt)
+ strncat(*dst, map + bt->start, blen);
+}
+
+static void fixname(char *s)
+{
+ for (; *s; s++)
+ *s = tolower(*s);
+}
+
+static void fixdesc(char *s)
+{
+ char *e = s + strlen(s);
+
+ /* Remove trailing dots that look ugly in perf list */
+ --e;
+ while (e >= s && isspace(*e))
+ --e;
+ if (*e == '.')
+ *e = 0;
+}
+
+static struct msrmap {
+ const char *num;
+ const char *pname;
+} msrmap[] = {
+ { "0x3F6", "ldlat=" },
+ { "0x1A6", "offcore_rsp=" },
+ { "0x1A7", "offcore_rsp=" },
+ { NULL, NULL }
+};
+
+static struct field {
+ const char *field;
+ const char *kernel;
+} fields[] = {
+ { "EventCode", "event=" },
+ { "UMask", "umask=" },
+ { "CounterMask", "cmask=" },
+ { "Invert", "inv=" },
+ { "AnyThread", "any=" },
+ { "EdgeDetect", "edge=" },
+ { "SampleAfterValue", "period=" },
+ { NULL, NULL }
+};
+
+static void cut_comma(char *map, jsmntok_t *newval)
+{
+ int i;
+
+ /* Cut off everything after comma */
+ for (i = newval->start; i < newval->end; i++) {
+ if (map[i] == ',')
+ newval->end = i;
+ }
+}
+
+static int match_field(char *map, jsmntok_t *field, int nz,
+ char **event, jsmntok_t *val)
+{
+ struct field *f;
+ jsmntok_t newval = *val;
+
+ for (f = fields; f->field; f++)
+ if (json_streq(map, field, f->field) && nz) {
+ cut_comma(map, &newval);
+ addfield(map, event, ",", f->kernel, &newval);
+ return 1;
+ }
+ return 0;
+}
+
+static struct msrmap *lookup_msr(char *map, jsmntok_t *val)
+{
+ jsmntok_t newval = *val;
+ static bool warned;
+ int i;
+
+ cut_comma(map, &newval);
+ for (i = 0; msrmap[i].num; i++)
+ if (json_streq(map, &newval, msrmap[i].num))
+ return &msrmap[i];
+ if (!warned) {
+ warned = true;
+ pr_err("%s: Unknown MSR in event file %.*s\n", prog,
+ json_len(val), map + val->start);
+ }
+ return NULL;
+}
+
+#define EXPECT(e, t, m) do { if (!(e)) { \
+ jsmntok_t *loc = (t); \
+ if (!(t)->start && (t) > tokens) \
+ loc = (t) - 1; \
+ pr_err("%s:%d: " m ", got %s\n", fn, \
+ json_line(map, loc), \
+ json_name(t)); \
+ goto out_free; \
+} } while (0)
+
+static void print_events_table_prefix(FILE *fp, const char *tblname)
+{
+ fprintf(fp, "struct pmu_event %s[] = {\n", tblname);
+}
+
+static int print_events_table_entry(void *data, char *name, char *event,
+ char *desc)
+{
+ FILE *outfp = data;
+ /*
+ * TODO: Remove formatting chars after debugging to reduce
+ * string lengths.
+ */
+ fprintf(outfp, "{\n");
+
+ fprintf(outfp, "\t.name = \"%s\",\n", name);
+ fprintf(outfp, "\t.event = \"%s\",\n", event);
+ fprintf(outfp, "\t.desc = \"%s\",\n", desc);
+
+ fprintf(outfp, "},\n");
+
+ return 0;
+}
+
+static void print_events_table_suffix(FILE *outfp)
+{
+ fprintf(outfp, "{\n");
+
+ fprintf(outfp, "\t.name = 0,\n");
+ fprintf(outfp, "\t.event = 0,\n");
+ fprintf(outfp, "\t.desc = 0,\n");
+
+ fprintf(outfp, "},\n");
+ fprintf(outfp, "};\n");
+}
+
+/* Call func with each event in the json file */
+int json_events(const char *fn,
+ int (*func)(void *data, char *name, char *event, char *desc),
+ void *data)
+{
+ int err = -EIO;
+ size_t size;
+ jsmntok_t *tokens, *tok;
+ int i, j, len;
+ char *map;
+
+ if (!fn)
+ return -ENOENT;
+
+ tokens = parse_json(fn, &map, &size, &len);
+ if (!tokens)
+ return -EIO;
+ EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array");
+ tok = tokens + 1;
+ for (i = 0; i < tokens->size; i++) {
+ char *event = NULL, *desc = NULL, *name = NULL;
+ struct msrmap *msr = NULL;
+ jsmntok_t *msrval = NULL;
+ jsmntok_t *precise = NULL;
+ jsmntok_t *obj = tok++;
+
+ EXPECT(obj->type == JSMN_OBJECT, obj, "expected object");
+ for (j = 0; j < obj->size; j += 2) {
+ jsmntok_t *field, *val;
+ int nz;
+
+ field = tok + j;
+ EXPECT(field->type == JSMN_STRING, tok + j,
+ "Expected field name");
+ val = tok + j + 1;
+ EXPECT(val->type == JSMN_STRING, tok + j + 1,
+ "Expected string value");
+
+ nz = !json_streq(map, val, "0");
+ if (match_field(map, field, nz, &event, val)) {
+ /* ok */
+ } else if (json_streq(map, field, "EventName")) {
+ addfield(map, &name, "", "", val);
+ } else if (json_streq(map, field, "BriefDescription")) {
+ addfield(map, &desc, "", "", val);
+ fixdesc(desc);
+ } else if (json_streq(map, field, "PEBS") && nz) {
+ precise = val;
+ } else if (json_streq(map, field, "MSRIndex") && nz) {
+ msr = lookup_msr(map, val);
+ } else if (json_streq(map, field, "MSRValue")) {
+ msrval = val;
+ } else if (json_streq(map, field, "Errata") &&
+ !json_streq(map, val, "null")) {
+ addfield(map, &desc, ". ",
+ " Spec update: ", val);
+ } else if (json_streq(map, field, "Data_LA") && nz) {
+ addfield(map, &desc, ". ",
+ " Supports address when precise",
+ NULL);
+ }
+ /* ignore unknown fields */
+ }
+ if (precise && !strstr(desc, "(Precise Event)")) {
+ if (json_streq(map, precise, "2"))
+ addfield(map, &desc, " ", "(Must be precise)",
+ NULL);
+ else
+ addfield(map, &desc, " ",
+ "(Precise event)", NULL);
+ }
+ if (msr != NULL)
+ addfield(map, &event, ",", msr->pname, msrval);
+ fixname(name);
+ err = func(data, name, event, desc);
+ free(event);
+ free(desc);
+ free(name);
+ if (err)
+ break;
+ tok += j;
+ }
+ EXPECT(tok - tokens == len, tok, "unexpected objects at end");
+ err = 0;
+out_free:
+ free_json(map, size, tokens);
+ return err;
+}
+
+static char *file_name_to_table_name(char *fname)
+{
+ unsigned int i, j;
+ int c;
+ int n = 1024; /* use max variable length? */
+ char *tblname;
+ char *p;
+
+ tblname = malloc(n);
+ if (!tblname)
+ return NULL;
+
+ p = basename(fname);
+
+ memset(tblname, 0, n);
+
+ /* Ensure table name starts with an alphabetic char */
+ strcpy(tblname, "pme_");
+
+ n = strlen(fname) + strlen(tblname);
+ n = min(1024, n);
+
+ for (i = 0, j = strlen(tblname); i < strlen(fname); i++, j++) {
+ c = p[i];
+ if (isalnum(c) || c == '_')
+ tblname[j] = c;
+ else if (c == '-')
+ tblname[j] = '_';
+ else if (c == '.') {
+ tblname[j] = '\0';
+ break;
+ } else {
+ pr_err("%s: Invalid character '%c' in file name %s\n",
+ prog, c, p);
+ free(tblname);
+ return NULL;
+ }
+ }
+
+ return tblname;
+}
+
+static void print_mapping_table_prefix(FILE *outfp)
+{
+ fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n");
+}
+
+static void print_mapping_table_suffix(FILE *outfp)
+{
+ /*
+ * Print the terminating, NULL entry.
+ */
+ fprintf(outfp, "{\n");
+ fprintf(outfp, "\t.cpuid = 0,\n");
+ fprintf(outfp, "\t.version = 0,\n");
+ fprintf(outfp, "\t.type = 0,\n");
+ fprintf(outfp, "\t.table = 0,\n");
+ fprintf(outfp, "},\n");
+
+ /* and finally, the closing curly bracket for the struct */
+ fprintf(outfp, "};\n");
+}
+
+/*
+ * Process the JSON file @json_file and write a table of PMU events found in
+ * the JSON file to the outfp.
+ */
+static int process_json(FILE *outfp, const char *json_file)
+{
+ char *tblname;
+ int err;
+
+ /*
+ * Drop file name suffix. Replace hyphens with underscores.
+ * Fail if file name contains any alphanum characters besides
+ * underscores.
+ */
+ tblname = file_name_to_table_name((char *)json_file);
+ if (!tblname) {
+ pr_info("%s: Error determining table name for %s\n", prog,
+ json_file);
+ return -1;
+ }
+
+ print_events_table_prefix(outfp, tblname);
+
+ err = json_events(json_file, print_events_table_entry, outfp);
+
+ if (err) {
+ pr_info("%s: Translation failed\n", prog);
+ return -1;
+ }
+
+ print_events_table_suffix(outfp);
+
+ return 0;
+}
+
+static int process_mapfile(FILE *outfp, char *fpath)
+{
+ int n = 16384;
+ FILE *mapfp;
+ char *save;
+ char *line, *p;
+ int line_num;
+ char *tblname;
+
+ pr_info("%s: Processing mapfile %s\n", prog, fpath);
+
+ line = malloc(n);
+ if (!line)
+ return -1;
+
+ mapfp = fopen(fpath, "r");
+ if (!mapfp) {
+ pr_info("%s: Error %s opening %s\n", prog, strerror(errno),
+ fpath);
+ return -1;
+ }
+
+ print_mapping_table_prefix(outfp);
+
+ line_num = 0;
+ while (1) {
+ char *cpuid, *version, *type, *fname;
+
+ line_num++;
+ p = fgets(line, n, mapfp);
+ if (!p)
+ break;
+
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ if (line[strlen(line)-1] != '\n') {
+ /* TODO Deal with lines longer than 16K */
+ pr_info("%s: Mapfile %s: line %d too long, aborting\n",
+ prog, fpath, line_num);
+ return -1;
+ }
+ line[strlen(line)-1] = '\0';
+
+ cpuid = strtok_r(p, ",", &save);
+ version = strtok_r(NULL, ",", &save);
+ fname = strtok_r(NULL, ",", &save);
+ type = strtok_r(NULL, ",", &save);
+
+ tblname = file_name_to_table_name(fname);
+ fprintf(outfp, "{\n");
+ fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid);
+ fprintf(outfp, "\t.version = \"%s\",\n", version);
+ fprintf(outfp, "\t.type = \"%s\",\n", type);
+
+ /*
+ * CHECK: We can't use the type (eg "core") field in the
+ * table name. For us to do that, we need to somehow tweak
+ * the other caller of file_name_to_table(), process_json()
+ * to determine the type. process_json() file has no way
+ * of knowing these are "core" events unless file name has
+ * core in it. If filename has core in it, we can safely
+ * ignore the type field here also.
+ */
+ fprintf(outfp, "\t.table = %s\n", tblname);
+ fprintf(outfp, "},\n");
+ }
+
+ print_mapping_table_suffix(outfp);
+
+ return 0;
+}
+
+/*
+ * If we fail to locate/process JSON and map files, create a NULL mapping
+ * table. This would at least allow perf to build even if we can't find/use
+ * the aliases.
+ */
+static void create_empty_mapping(const char *output_file)
+{
+ FILE *outfp;
+
+ pr_info("%s: Creating empty pmu_events_map[] table\n", prog);
+
+ /* Unlink file to clear any partial writes to it */
+ unlink(output_file);
+
+ outfp = fopen(output_file, "a");
+ if (!outfp) {
+ perror("fopen()");
+ _Exit(1);
+ }
+
+ fprintf(outfp, "#include \"../../pmu-events/pmu-events.h\"\n");
+ print_mapping_table_prefix(outfp);
+ print_mapping_table_suffix(outfp);
+ fclose(outfp);
+}
+
+static int get_maxfds(void)
+{
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
+ return rlim.rlim_max;
+
+ return 512;
+}
+
+/*
+ * nftw() doesn't let us pass an argument to the processing function,
+ * so use a global variables.
+ */
+FILE *eventsfp;
+char *mapfile;
+
+static int process_one_file(const char *fpath, const struct stat *sb,
+ int typeflag __maybe_unused,
+ struct FTW *ftwbuf __maybe_unused)
+{
+ char *bname;
+
+ if (!S_ISREG(sb->st_mode))
+ return 0;
+
+ /*
+ * Save the mapfile name for now. We will process mapfile
+ * after processing all JSON files (so we can write out the
+ * mapping table after all PMU events tables).
+ *
+ * TODO: Allow for multiple mapfiles? Punt for now.
+ */
+ bname = basename((char *)fpath);
+ if (!strncmp(bname, "mapfile.csv", 11)) {
+ if (mapfile) {
+ pr_info("%s: Many mapfiles? Using %s, ignoring %s\n",
+ prog, mapfile, fpath);
+ } else {
+ mapfile = strdup(fpath);
+ }
+ return 0;
+ }
+
+ /*
+ * If the file name does not have a .json extension,
+ * ignore it. It could be a readme.txt for instance.
+ */
+ bname += strlen(bname) - 5;
+ if (strncmp(bname, ".json", 5)) {
+ pr_info("%s: Ignoring file without .json suffix %s\n", prog,
+ fpath);
+ return 0;
+ }
+
+ /*
+ * Assume all other files are JSON files.
+ *
+ * If mapfile refers to 'power7_core.json', we create a table
+ * named 'power7_core'. Any inconsistencies between the mapfile
+ * and directory tree could result in build failure due to table
+ * names not being found.
+ *
+ * Atleast for now, be strict with processing JSON file names.
+ * i.e. if JSON file name cannot be mapped to C-style table name,
+ * fail.
+ */
+ if (process_json(eventsfp, fpath)) {
+ pr_info("%s: Error processing JSON file %s, ignoring all\n",
+ prog, fpath);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+/*
+ * Starting in directory 'start_dirname', find the "mapfile.csv" and
+ * the set of JSON files for the architecture 'arch'.
+ *
+ * From each JSON file, create a C-style "PMU events table" from the
+ * JSON file (see struct pmu_event).
+ *
+ * From the mapfile, create a mapping between the CPU revisions and
+ * PMU event tables (see struct pmu_events_map).
+ *
+ * Write out the PMU events tables and the mapping table to pmu-event.c.
+ *
+ * If unable to process the JSON or arch files, create an empty mapping
+ * table so we can continue to build/use perf even if we cannot use the
+ * PMU event aliases.
+ */
+int main(int argc, char *argv[])
+{
+ int rc;
+ int flags;
+ int maxfds;
+ char dirname[PATH_MAX];
+
+ const char *arch;
+ const char *output_file;
+ const char *start_dirname;
+
+ prog = basename(argv[0]);
+ if (argc < 4) {
+ pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog);
+ return 1;
+ }
+
+ arch = argv[1];
+ start_dirname = argv[2];
+ output_file = argv[3];
+
+ if (argc > 4)
+ verbose = atoi(argv[4]);
+
+ unlink(output_file);
+ eventsfp = fopen(output_file, "a");
+ if (!eventsfp) {
+ pr_err("%s Unable to create required file %s (%s)\n",
+ prog, output_file, strerror(errno));
+ return 2;
+ }
+
+ /* Include pmu-events.h first */
+ fprintf(eventsfp, "#include \"../../pmu-events/pmu-events.h\"\n");
+
+ sprintf(dirname, "%s/%s", start_dirname, arch);
+
+ /*
+ * The mapfile allows multiple CPUids to point to the same JSON file,
+ * so, not sure if there is a need for symlinks within the pmu-events
+ * directory.
+ *
+ * For now, treat symlinks of JSON files as regular files and create
+ * separate tables for each symlink (presumably, each symlink refers
+ * to specific version of the CPU).
+ */
+ flags = FTW_DEPTH;
+
+ maxfds = get_maxfds();
+ mapfile = NULL;
+ rc = nftw(dirname, process_one_file, maxfds, flags);
+ if (rc && verbose) {
+ pr_info("%s: Error walking file tree %s\n", prog, dirname);
+ goto empty_map;
+ } else if (rc) {
+ goto empty_map;
+ }
+
+ if (!mapfile) {
+ pr_info("%s: No CPU->JSON mapping?\n", prog);
+ goto empty_map;
+ }
+
+ if (process_mapfile(eventsfp, mapfile)) {
+ pr_info("%s: Error processing mapfile %s\n", prog, mapfile);
+ goto empty_map;
+ }
+
+ return 0;
+
+empty_map:
+ fclose(eventsfp);
+ create_empty_mapping(output_file);
+ return 0;
+}
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h
new file mode 100644
index 0000000..996601f
--- /dev/null
+++ b/tools/perf/pmu-events/jevents.h
@@ -0,0 +1,17 @@
+#ifndef JEVENTS_H
+#define JEVENTS_H 1
+
+int json_events(const char *fn,
+ int (*func)(void *data, char *name, char *event, char *desc),
+ void *data);
+char *get_cpu_str(void);
+
+#ifndef min
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#endif
diff --git a/tools/perf/pmu-events/json.h b/tools/perf/pmu-events/json.h
index 6b8337e..5ac88ec 100644
--- a/tools/perf/pmu-events/json.h
+++ b/tools/perf/pmu-events/json.h
@@ -24,6 +24,9 @@ extern int eprintf(int level, int var, const char *fmt, ...);
#define pr_err(fmt, ...) \
eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)

+#define pr_info(fmt, ...) \
+ eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__)
+
#ifndef roundup
#define roundup(x, y) ( \
{ \
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
new file mode 100644
index 0000000..39fec04
--- /dev/null
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -0,0 +1,35 @@
+#ifndef PMU_EVENTS_H
+#define PMU_EVENTS_H
+
+/*
+ * Describe each PMU event. Each CPU has a table of PMU events.
+ */
+struct pmu_event {
+ const char *name;
+ const char *event;
+ const char *desc;
+};
+
+/*
+ *
+ * Map a CPU to its table of PMU events. The CPU is identified by the
+ * cpuid field, which is an arch-specific identifier for the CPU.
+ * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile
+ * must match the get_cpustr() in tools/perf/arch/xxx/util/header.c)
+ *
+ * The cpuid can contain any character other than the comma.
+ */
+struct pmu_events_map {
+ const char *cpuid;
+ const char *version;
+ const char *type; /* core, uncore etc */
+ struct pmu_event *table;
+};
+
+/*
+ * Global table mapping each known CPU for the architecture to its
+ * table of PMU events.
+ */
+extern struct pmu_events_map pmu_events_map[];
+
+#endif
--
1.7.9.5

2015-06-02 17:13:52

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 03/14] perf, tools: Use pmu_events_map table to create event aliases

At run time, (i.e when perf is starting up), locate the specific events
table for the current CPU and create event aliases for each of the events.

Use these aliases to parse user's specified perf event.

Signed-off-by: Sukadev Bhattiprolu <[email protected]>

Changelog[v3]
[Jiri Olsa] Fix a memory leak with cpuid.

Changelog[v2]
[Andi Kleen] Replace the pmu_events_map->vfm field with a simple
generic "cpuid" string and use that string to find the
matching mapfile entry.
---
tools/perf/arch/powerpc/util/header.c | 11 ++++
tools/perf/util/header.h | 3 +-
tools/perf/util/pmu.c | 104 ++++++++++++++++++++++++++++-----
3 files changed, 104 insertions(+), 14 deletions(-)

diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c
index 6c1b8a7..65f9391 100644
--- a/tools/perf/arch/powerpc/util/header.c
+++ b/tools/perf/arch/powerpc/util/header.c
@@ -32,3 +32,14 @@ get_cpuid(char *buffer, size_t sz)
}
return -1;
}
+
+char *
+get_cpuid_str(void)
+{
+ char *bufp;
+
+ if (asprintf(&bufp, "%.8lx", mfspr(SPRN_PVR)) < 0)
+ bufp = NULL;
+
+ return bufp;
+}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index d4d5796..bb2ef5e 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -8,7 +8,6 @@
#include <linux/types.h>
#include "event.h"

-
enum {
HEADER_RESERVED = 0, /* always cleared */
HEADER_FIRST_FEATURE = 1,
@@ -157,4 +156,6 @@ int write_padded(int fd, const void *bf, size_t count, size_t count_aligned);
*/
int get_cpuid(char *buffer, size_t sz);

+char *get_cpuid_str(void);
+
#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 0fcc624..0932323 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -10,7 +10,9 @@
#include "util.h"
#include "pmu.h"
#include "parse-events.h"
+#include "pmu-events/pmu-events.h"
#include "cpumap.h"
+#include "header.h"

struct perf_pmu_format {
char *name;
@@ -205,17 +207,11 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
return 0;
}

-static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
+static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir, char *desc __maybe_unused, char *val)
{
struct perf_pmu_alias *alias;
- char buf[256];
int ret;

- ret = fread(buf, 1, sizeof(buf), file);
- if (ret == 0)
- return -EINVAL;
- buf[ret] = 0;
-
alias = malloc(sizeof(*alias));
if (!alias)
return -ENOMEM;
@@ -225,26 +221,47 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
alias->unit[0] = '\0';
alias->per_pkg = false;

- ret = parse_events_terms(&alias->terms, buf);
+ ret = parse_events_terms(&alias->terms, val);
if (ret) {
+ pr_err("Cannot parse alias %s: %d\n", val, ret);
free(alias);
return ret;
}

alias->name = strdup(name);
+ if (dir) {
+ /*
+ * load unit name and scale if available
+ */
+ perf_pmu__parse_unit(alias, dir, name);
+ perf_pmu__parse_scale(alias, dir, name);
+ perf_pmu__parse_per_pkg(alias, dir, name);
+ perf_pmu__parse_snapshot(alias, dir, name);
+ }
+
/*
- * load unit name and scale if available
+ * TODO: pickup description from Andi's patchset
*/
- perf_pmu__parse_unit(alias, dir, name);
- perf_pmu__parse_scale(alias, dir, name);
- perf_pmu__parse_per_pkg(alias, dir, name);
- perf_pmu__parse_snapshot(alias, dir, name);
+ //alias->desc = desc ? strdpu(desc) : NULL;

list_add_tail(&alias->list, list);

return 0;
}

+static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
+{
+ char buf[256];
+ int ret;
+
+ ret = fread(buf, 1, sizeof(buf), file);
+ if (ret == 0)
+ return -EINVAL;
+ buf[ret] = 0;
+
+ return __perf_pmu__new_alias(list, name, dir, NULL, buf);
+}
+
static inline bool pmu_alias_info_file(char *name)
{
size_t len;
@@ -442,6 +459,65 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
return NULL;
}

+/*
+ * Return the CPU id as a raw string.
+ *
+ * Each architecture should provide a more precise id string that
+ * can be use to match the architecture's "mapfile".
+ */
+char *__attribute__((weak))get_cpuid_str(void)
+{
+ return NULL;
+}
+
+/*
+ * From the pmu_events_map, find the table of PMU events that corresponds
+ * to the current running CPU. Then, add all PMU events from that table
+ * as aliases.
+ */
+static int pmu_add_cpu_aliases(void *data)
+{
+ struct list_head *head = (struct list_head *)data;
+ int i;
+ struct pmu_events_map *map;
+ struct pmu_event *pe;
+ char *cpuid;
+
+ cpuid = get_cpuid_str();
+ if (!cpuid)
+ return 0;
+
+ i = 0;
+ while (1) {
+ map = &pmu_events_map[i++];
+ if (!map->table) {
+ goto out;
+ }
+
+ if (!strcmp(map->cpuid, cpuid))
+ break;
+ }
+
+ /*
+ * Found a matching PMU events table. Create aliases
+ */
+ i = 0;
+ while (1) {
+ pe = &map->table[i++];
+ if (!pe->name)
+ break;
+
+ /* need type casts to override 'const' */
+ __perf_pmu__new_alias(head, (char *)pe->name, NULL,
+ (char *)pe->desc, (char *)pe->event);
+ }
+
+out:
+ free(cpuid);
+ return 0;
+}
+
+
static struct perf_pmu *pmu_lookup(const char *name)
{
struct perf_pmu *pmu;
@@ -464,6 +540,8 @@ static struct perf_pmu *pmu_lookup(const char *name)
if (pmu_aliases(name, &aliases))
return NULL;

+ if (!strcmp(name, "cpu"))
+ (void)pmu_add_cpu_aliases(&aliases);
if (pmu_type(name, &type))
return NULL;

--
1.7.9.5

2015-06-02 17:13:38

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 04/14] perf, tools: Allow events with dot

From: Andi Kleen <[email protected]>

The Intel events use a dot to separate event name and unit mask.
Allow dot in names in the scanner, and remove special handling
of dot as EOF. Also remove the hack in jevents to replace dot
with underscore. This way dotted events can be specified
directly by the user.

I'm not fully sure this change to the scanner is correct
(what was the dot special case good for?), but I haven't
found anything that breaks with it so far at least.

V2: Add the dot to name too, to handle events outside cpu//
Acked-by: Namhyung Kim <[email protected]>
Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>
---
tools/perf/util/parse-events.l | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 09e738f..13cef3c 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -119,8 +119,8 @@ event [^,{}/]+
num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
num_raw_hex [a-fA-F0-9]+
-name [a-zA-Z_*?][a-zA-Z0-9_*?]*
-name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]*
+name [a-zA-Z_*?][a-zA-Z0-9_*?.]*
+name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]*
/* If you add a modifier you need to update check_modifier() */
modifier_event [ukhpGHSDI]+
modifier_bp [rwx]{1,3}
@@ -165,7 +165,6 @@ modifier_bp [rwx]{1,3}
return PE_EVENT_NAME;
}

-. |
<<EOF>> {
BEGIN(INITIAL);
REWIND(0);
--
1.7.9.5

2015-06-02 17:15:48

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 05/14] perf, tools: Support CPU id matching for x86 v2

From: Andi Kleen <[email protected]>

Implement the code to match CPU types to mapfile types for x86
based on CPUID. This extends an existing similar function,
but changes it to use the x86 mapfile cpu description.
This allows to resolve event lists generated by jevents.

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

v2: Update to new get_cpuid_str() interface
---
tools/perf/arch/x86/util/header.c | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
index 146d12a..a74a48d 100644
--- a/tools/perf/arch/x86/util/header.c
+++ b/tools/perf/arch/x86/util/header.c
@@ -19,8 +19,8 @@ cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
: "a" (op));
}

-int
-get_cpuid(char *buffer, size_t sz)
+static int
+__get_cpuid(char *buffer, size_t sz, const char *fmt)
{
unsigned int a, b, c, d, lvl;
int family = -1, model = -1, step = -1;
@@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz)
if (family >= 0x6)
model += ((a >> 16) & 0xf) << 4;
}
- nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step);
+ nb = scnprintf(buffer, sz, fmt, vendor, family, model, step);

/* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) {
@@ -57,3 +57,21 @@ get_cpuid(char *buffer, size_t sz)
}
return -1;
}
+
+int
+get_cpuid(char *buffer, size_t sz)
+{
+ return __get_cpuid(buffer, sz, "%s,%u,%u,%u$");
+}
+
+char *
+get_cpuid_str(void)
+{
+ char *buf = malloc(128);
+
+ if (__get_cpuid(buf, 128, "%s-%u-%X$") < 0) {
+ free(buf);
+ return NULL;
+ }
+ return buf;
+}
--
1.7.9.5

2015-06-02 17:15:42

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 06/14] perf, tools: Support alias descriptions

From: Andi Kleen <[email protected]>

Add support to print alias descriptions in perf list, which
are taken from the generated event files.

The sorting code is changed to put the events with descriptions
at the end. The descriptions are printed as possibly multiple word
wrapped lines.

Example output:

% perf list
...
arith.fpu_div
[Divide operations executed]
arith.fpu_div_active
[Cycles when divider is busy executing divide operations]

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

Changelog
- Delete a redundant free()
---
tools/perf/util/pmu.c | 83 +++++++++++++++++++++++++++++++++++++------------
tools/perf/util/pmu.h | 1 +
2 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 0932323..77b6801 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -239,10 +239,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir,
perf_pmu__parse_snapshot(alias, dir, name);
}

- /*
- * TODO: pickup description from Andi's patchset
- */
- //alias->desc = desc ? strdpu(desc) : NULL;
+ alias->desc = desc ? strdup(desc) : NULL;

list_add_tail(&alias->list, list);

@@ -993,11 +990,42 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu,
return buf;
}

-static int cmp_string(const void *a, const void *b)
+struct pair {
+ char *name;
+ char *desc;
+};
+
+static int cmp_pair(const void *a, const void *b)
{
- const char * const *as = a;
- const char * const *bs = b;
- return strcmp(*as, *bs);
+ const struct pair *as = a;
+ const struct pair *bs = b;
+
+ /* Put extra events last */
+ if (!!as->desc != !!bs->desc)
+ return !!as->desc - !!bs->desc;
+ return strcmp(as->name, bs->name);
+}
+
+static void wordwrap(char *s, int start, int max, int corr)
+{
+ int column = start;
+ int n;
+
+ while (*s) {
+ int wlen = strcspn(s, " \t");
+
+ if (column + wlen >= max && column > start) {
+ printf("\n%*s", start, "");
+ column = start + corr;
+ }
+ n = printf("%s%.*s", column > start ? " " : "", wlen, s);
+ if (n <= 0)
+ break;
+ s += wlen;
+ column += n;
+ while (isspace(*s))
+ s++;
+ }
}

void print_pmu_events(const char *event_glob, bool name_only)
@@ -1007,7 +1035,9 @@ void print_pmu_events(const char *event_glob, bool name_only)
char buf[1024];
int printed = 0;
int len, j;
- char **aliases;
+ struct pair *aliases;
+ int numdesc = 0;
+ int columns = 78;

pmu = NULL;
len = 0;
@@ -1017,14 +1047,15 @@ void print_pmu_events(const char *event_glob, bool name_only)
if (pmu->selectable)
len++;
}
- aliases = zalloc(sizeof(char *) * len);
+ aliases = zalloc(sizeof(struct pair) * len);
if (!aliases)
goto out_enomem;
pmu = NULL;
j = 0;
while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list) {
- char *name = format_alias(buf, sizeof(buf), pmu, alias);
+ char *name = alias->desc ? alias->name :
+ format_alias(buf, sizeof(buf), pmu, alias);
bool is_cpu = !strcmp(pmu->name, "cpu");

if (event_glob != NULL &&
@@ -1033,37 +1064,49 @@ void print_pmu_events(const char *event_glob, bool name_only)
event_glob))))
continue;

- if (is_cpu && !name_only)
+ if (is_cpu && !name_only && !alias->desc)
name = format_alias_or(buf, sizeof(buf), pmu, alias);

- aliases[j] = strdup(name);
- if (aliases[j] == NULL)
- goto out_enomem;
+ aliases[j].name = name;
+ if (is_cpu && !name_only && !alias->desc)
+ aliases[j].name = format_alias_or(buf, sizeof(buf),
+ pmu, alias);
+ aliases[j].name = strdup(aliases[j].name);
+ /* failure harmless */
+ aliases[j].desc = alias->desc;
j++;
}
if (pmu->selectable) {
char *s;
if (asprintf(&s, "%s//", pmu->name) < 0)
goto out_enomem;
- aliases[j] = s;
+ aliases[j].name = s;
j++;
}
}
len = j;
- qsort(aliases, len, sizeof(char *), cmp_string);
+ qsort(aliases, len, sizeof(struct pair), cmp_pair);
for (j = 0; j < len; j++) {
if (name_only) {
- printf("%s ", aliases[j]);
+ printf("%s ", aliases[j].name);
continue;
}
- printf(" %-50s [Kernel PMU event]\n", aliases[j]);
+ if (aliases[j].desc) {
+ if (numdesc++ == 0)
+ printf("\n");
+ printf(" %-50s\n", aliases[j].name);
+ printf("%*s", 8, "[");
+ wordwrap(aliases[j].desc, 8, columns, 0);
+ printf("]\n");
+ } else
+ printf(" %-50s [Kernel PMU event]\n", aliases[j].name);
printed++;
}
if (printed)
printf("\n");
out_free:
for (j = 0; j < len; j++)
- zfree(&aliases[j]);
+ zfree(&aliases[j].name);
zfree(&aliases);
return;

diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 7b9c8cf..e7b1010 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -38,6 +38,7 @@ struct perf_pmu_info {

struct perf_pmu_alias {
char *name;
+ char *desc;
struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1];
--
1.7.9.5

2015-06-02 17:15:34

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 07/14] perf, tools: Query terminal width and use in perf list

From: Andi Kleen <[email protected]>

Automatically adapt the now wider and word wrapped perf list
output to wider terminals. This requires querying the terminal
before the auto pager takes over, and exporting this
information from the pager subsystem.

Acked-by: Namhyung Kim <[email protected]>
Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

Conflicts:
tools/perf/util/pager.c
---
tools/perf/util/cache.h | 1 +
tools/perf/util/pager.c | 15 +++++++++++++++
tools/perf/util/pmu.c | 3 ++-
3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index c861373..8e0d4b8 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -32,6 +32,7 @@ extern const char *perf_config_dirname(const char *, const char *);
extern void setup_pager(void);
extern int pager_in_use(void);
extern int pager_use_color;
+int pager_get_columns(void);

char *alias_lookup(const char *alias);
int split_cmdline(char *cmdline, const char ***argv);
diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c
index 53ef006..1770c88 100644
--- a/tools/perf/util/pager.c
+++ b/tools/perf/util/pager.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "run-command.h"
#include "sigchain.h"
+#include <sys/ioctl.h>

/*
* This is split up from the rest of git so that we can do
@@ -8,6 +9,7 @@
*/

static int spawned_pager;
+static int pager_columns;

static void pager_preexec(void)
{
@@ -47,9 +49,12 @@ static void wait_for_pager_signal(int signo)
void setup_pager(void)
{
const char *pager = getenv("PERF_PAGER");
+ struct winsize sz;

if (!isatty(1))
return;
+ if (ioctl(1, TIOCGWINSZ, &sz) == 0)
+ pager_columns = sz.ws_col;
if (!pager)
pager = getenv("PAGER");
if (!(pager || access("/usr/bin/pager", X_OK)))
@@ -93,3 +98,13 @@ int pager_in_use(void)
env = getenv("PERF_PAGER_IN_USE");
return env ? perf_config_bool("PERF_PAGER_IN_USE", env) : 0;
}
+
+int pager_get_columns(void)
+{
+ char *s;
+
+ s = getenv("COLUMNS");
+ if (s)
+ return atoi(s);
+ return (pager_columns ? pager_columns : 80) - 2;
+}
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 77b6801..77e0500 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -13,6 +13,7 @@
#include "pmu-events/pmu-events.h"
#include "cpumap.h"
#include "header.h"
+#include "cache.h"

struct perf_pmu_format {
char *name;
@@ -1037,7 +1038,7 @@ void print_pmu_events(const char *event_glob, bool name_only)
int len, j;
struct pair *aliases;
int numdesc = 0;
- int columns = 78;
+ int columns = pager_get_columns();

pmu = NULL;
len = 0;
--
1.7.9.5

2015-06-02 17:15:29

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 08/14] perf, tools: Add a --no-desc flag to perf list

From: Andi Kleen <[email protected]>

Add a --no-desc flag to perf list to not print the event descriptions
that were earlier added for JSON events. This may be useful to
get a less crowded listing.

It's still default to print descriptions as that is the more useful
default for most users.

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

v2: Rename --quiet to --no-desc. Add option to man page.
---
tools/perf/Documentation/perf-list.txt | 8 +++++++-
tools/perf/builtin-list.c | 12 ++++++++----
tools/perf/util/parse-events.c | 4 ++--
tools/perf/util/parse-events.h | 2 +-
tools/perf/util/pmu.c | 4 ++--
tools/perf/util/pmu.h | 2 +-
6 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index bada893..9507552 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -8,13 +8,19 @@ perf-list - List all symbolic event types
SYNOPSIS
--------
[verse]
-'perf list' [hw|sw|cache|tracepoint|pmu|event_glob]
+'perf list' [--no-desc] [hw|sw|cache|tracepoint|pmu|event_glob]

DESCRIPTION
-----------
This command displays the symbolic event types which can be selected in the
various perf commands with the -e option.

+OPTIONS
+-------
+--no-desc::
+Don't print descriptions.
+
+
[[EVENT_MODIFIERS]]
EVENT MODIFIERS
---------------
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index af5bd05..3f058f7 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -16,16 +16,20 @@
#include "util/pmu.h"
#include "util/parse-options.h"

+static bool desc_flag = true;
+
int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
{
int i;
bool raw_dump = false;
struct option list_options[] = {
OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"),
+ OPT_BOOLEAN('d', "desc", &desc_flag,
+ "Print extra event descriptions. --no-desc to not print."),
OPT_END()
};
const char * const list_usage[] = {
- "perf list [hw|sw|cache|tracepoint|pmu|event_glob]",
+ "perf list [--no-desc] [hw|sw|cache|tracepoint|pmu|event_glob]",
NULL
};

@@ -40,7 +44,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
printf("\nList of pre-defined events (to be used in -e):\n\n");

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

@@ -59,13 +63,13 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(NULL, raw_dump);
else if (strcmp(argv[i], "pmu") == 0)
- print_pmu_events(NULL, raw_dump);
+ print_pmu_events(NULL, raw_dump, !desc_flag);
else {
char *sep = strchr(argv[i], ':'), *s;
int sep_idx;

if (sep == NULL) {
- print_events(argv[i], raw_dump);
+ print_events(argv[i], raw_dump, !desc_flag);
continue;
}
sep_idx = sep - argv[i];
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 2a4d1ec..65f7572 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1521,7 +1521,7 @@ out_enomem:
/*
* Print the help text for the event symbols:
*/
-void print_events(const char *event_glob, bool name_only)
+void print_events(const char *event_glob, bool name_only, bool quiet_flag)
{
print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
@@ -1531,7 +1531,7 @@ void print_events(const char *event_glob, bool name_only)

print_hwcache_events(event_glob, name_only);

- print_pmu_events(event_glob, name_only);
+ print_pmu_events(event_glob, name_only, quiet_flag);

if (event_glob != NULL)
return;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 131f29b..d11f854 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -133,7 +133,7 @@ void parse_events_update_lists(struct list_head *list_event,
void parse_events_evlist_error(struct parse_events_evlist *data,
int idx, const char *str);

-void print_events(const char *event_glob, bool name_only);
+void print_events(const char *event_glob, bool name_only, bool quiet);

struct event_symbol {
const char *symbol;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 77e0500..417333b 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1029,7 +1029,7 @@ static void wordwrap(char *s, int start, int max, int corr)
}
}

-void print_pmu_events(const char *event_glob, bool name_only)
+void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
{
struct perf_pmu *pmu;
struct perf_pmu_alias *alias;
@@ -1092,7 +1092,7 @@ void print_pmu_events(const char *event_glob, bool name_only)
printf("%s ", aliases[j].name);
continue;
}
- if (aliases[j].desc) {
+ if (aliases[j].desc && !quiet_flag) {
if (numdesc++ == 0)
printf("\n");
printf(" %-50s\n", aliases[j].name);
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index e7b1010..9966c1a 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -69,7 +69,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head);

struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);

-void print_pmu_events(const char *event_glob, bool name_only);
+void print_pmu_events(const char *event_glob, bool name_only, bool quiet);
bool pmu_have_event(const char *pname, const char *name);

int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
--
1.7.9.5

2015-06-02 17:13:57

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 09/14] perf, tools: Group alias perf list by section

From: Andi Kleen <[email protected]>

The first first element in an hardware event name defines the general
area, usually the part of the micro architecture it is refering to.
Group the perf list output by these sections by adding section headers.

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>
---
tools/perf/util/pmu.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 417333b..8d91fa7 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1039,6 +1039,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
struct pair *aliases;
int numdesc = 0;
int columns = pager_get_columns();
+ const char *last_section;
+ int last_section_len;

pmu = NULL;
len = 0;
@@ -1086,6 +1088,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
}
}
len = j;
+ last_section = NULL;
+ last_section_len = 0;
qsort(aliases, len, sizeof(struct pair), cmp_pair);
for (j = 0; j < len; j++) {
if (name_only) {
@@ -1093,8 +1097,21 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
continue;
}
if (aliases[j].desc && !quiet_flag) {
+ char *dot;
+ char *name = aliases[j].name;
+
if (numdesc++ == 0)
printf("\n");
+ dot = strchr(name, '.');
+ if (dot &&
+ (!last_section ||
+ strncmp(last_section, name, last_section_len))) {
+ last_section_len = dot - name;
+ printf("%s%.*s:\n",
+ last_section ? "\n" : "",
+ last_section_len, name);
+ last_section = name;
+ }
printf(" %-50s\n", aliases[j].name);
printf("%*s", 8, "[");
wordwrap(aliases[j].desc, 8, columns, 0);
--
1.7.9.5

2015-06-02 17:15:20

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 10/14] perf, tools: Add override support for event list CPUID

From: Andi Kleen <[email protected]>

Add a PERF_CPUID variable to override the CPUID of the current CPU.
This is useful for testing, so that all event lists can be tested on a single
system.

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

v2: Fix double free in earlier version.
Print actual CPUID being used with verbose option.
---
tools/perf/util/pmu.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 8d91fa7..2cf03a7 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -481,10 +481,16 @@ static int pmu_add_cpu_aliases(void *data)
struct pmu_event *pe;
char *cpuid;

- cpuid = get_cpuid_str();
+ cpuid = getenv("PERF_CPUID");
+ if (cpuid)
+ cpuid = strdup(cpuid);
+ if (!cpuid)
+ cpuid = get_cpuid_str();
if (!cpuid)
return 0;

+ pr_debug("Using CPUID %s\n", cpuid);
+
i = 0;
while (1) {
map = &pmu_events_map[i++];
--
1.7.9.5

2015-06-02 17:15:13

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 11/14] perf, tools: Support long descriptions with perf list -v

From: Andi Kleen <[email protected]>

Previously we were dropping the useful longer descriptions that some
events have in the event list completely. This patch makes them appear with
perf list.

Old perf list:

baclears:
baclears.all
[Counts the number of baclears]

vs new:

perf list -v:
...
baclears:
baclears.all
[The BACLEARS event counts the number of times the front end is resteered, mainly when
the Branch Prediction Unit cannot provide a correct prediction and this is corrected
by the Branch Address Calculator at the front end. The BACLEARS.ANY event counts the
number of baclears for any type of branch]

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>
---
tools/perf/builtin-list.c | 9 ++++++---
tools/perf/pmu-events/jevents.c | 29 ++++++++++++++++++++---------
tools/perf/pmu-events/jevents.h | 2 +-
tools/perf/pmu-events/pmu-events.h | 1 +
tools/perf/util/parse-events.c | 4 ++--
tools/perf/util/parse-events.h | 2 +-
tools/perf/util/pmu.c | 17 ++++++++++++-----
tools/perf/util/pmu.h | 4 +++-
8 files changed, 46 insertions(+), 22 deletions(-)

diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 3f058f7..c912368 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -22,10 +22,13 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
{
int i;
bool raw_dump = false;
+ bool long_desc_flag = false;
struct option list_options[] = {
OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"),
OPT_BOOLEAN('d', "desc", &desc_flag,
"Print extra event descriptions. --no-desc to not print."),
+ OPT_BOOLEAN('v', "long-desc", &long_desc_flag,
+ "Print longer event descriptions."),
OPT_END()
};
const char * const list_usage[] = {
@@ -44,7 +47,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
printf("\nList of pre-defined events (to be used in -e):\n\n");

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

@@ -63,13 +66,13 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(NULL, raw_dump);
else if (strcmp(argv[i], "pmu") == 0)
- print_pmu_events(NULL, raw_dump, !desc_flag);
+ print_pmu_events(NULL, raw_dump, !desc_flag, long_desc_flag);
else {
char *sep = strchr(argv[i], ':'), *s;
int sep_idx;

if (sep == NULL) {
- print_events(argv[i], raw_dump, !desc_flag);
+ print_events(argv[i], raw_dump, !desc_flag, long_desc_flag);
continue;
}
sep_idx = sep - argv[i];
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index ab83d85..8990d71 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -203,7 +203,7 @@ static void print_events_table_prefix(FILE *fp, const char *tblname)
}

static int print_events_table_entry(void *data, char *name, char *event,
- char *desc)
+ char *desc, char *long_desc)
{
FILE *outfp = data;
/*
@@ -215,7 +215,8 @@ static int print_events_table_entry(void *data, char *name, char *event,
fprintf(outfp, "\t.name = \"%s\",\n", name);
fprintf(outfp, "\t.event = \"%s\",\n", event);
fprintf(outfp, "\t.desc = \"%s\",\n", desc);
-
+ if (long_desc && long_desc[0])
+ fprintf(outfp, "\t.long_desc = \"%s\"", long_desc);
fprintf(outfp, "},\n");

return 0;
@@ -235,7 +236,7 @@ static void print_events_table_suffix(FILE *outfp)

/* Call func with each event in the json file */
int json_events(const char *fn,
- int (*func)(void *data, char *name, char *event, char *desc),
+ int (*func)(void *data, char *name, char *event, char *desc, char *long_desc),
void *data)
{
int err = -EIO;
@@ -253,7 +254,8 @@ int json_events(const char *fn,
EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array");
tok = tokens + 1;
for (i = 0; i < tokens->size; i++) {
- char *event = NULL, *desc = NULL, *name = NULL;
+ char *event = NULL, *desc = NULL, *name = NULL, *long_desc = NULL;
+ char *extra_desc = NULL;
struct msrmap *msr = NULL;
jsmntok_t *msrval = NULL;
jsmntok_t *precise = NULL;
@@ -279,6 +281,9 @@ int json_events(const char *fn,
} else if (json_streq(map, field, "BriefDescription")) {
addfield(map, &desc, "", "", val);
fixdesc(desc);
+ } else if (json_streq(map, field, "PublicDescription")) {
+ addfield(map, &long_desc, "", "", val);
+ fixdesc(long_desc);
} else if (json_streq(map, field, "PEBS") && nz) {
precise = val;
} else if (json_streq(map, field, "MSRIndex") && nz) {
@@ -287,10 +292,10 @@ int json_events(const char *fn,
msrval = val;
} else if (json_streq(map, field, "Errata") &&
!json_streq(map, val, "null")) {
- addfield(map, &desc, ". ",
+ addfield(map, &extra_desc, ". ",
" Spec update: ", val);
} else if (json_streq(map, field, "Data_LA") && nz) {
- addfield(map, &desc, ". ",
+ addfield(map, &extra_desc, ". ",
" Supports address when precise",
NULL);
}
@@ -298,19 +303,25 @@ int json_events(const char *fn,
}
if (precise && !strstr(desc, "(Precise Event)")) {
if (json_streq(map, precise, "2"))
- addfield(map, &desc, " ", "(Must be precise)",
+ addfield(map, &extra_desc, " ", "(Must be precise)",
NULL);
else
- addfield(map, &desc, " ",
+ addfield(map, &extra_desc, " ",
"(Precise event)", NULL);
}
+ if (desc && extra_desc)
+ addfield(map, &desc, " ", extra_desc, NULL);
+ if (long_desc && extra_desc)
+ addfield(map, &long_desc, " ", extra_desc, NULL);
if (msr != NULL)
addfield(map, &event, ",", msr->pname, msrval);
fixname(name);
- err = func(data, name, event, desc);
+ err = func(data, name, event, desc, long_desc);
free(event);
free(desc);
free(name);
+ free(extra_desc);
+ free(long_desc);
if (err)
break;
tok += j;
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h
index 996601f..2168ad4 100644
--- a/tools/perf/pmu-events/jevents.h
+++ b/tools/perf/pmu-events/jevents.h
@@ -2,7 +2,7 @@
#define JEVENTS_H 1

int json_events(const char *fn,
- int (*func)(void *data, char *name, char *event, char *desc),
+ int (*func)(void *data, char *name, char *event, char *desc, char *long_desc),
void *data);
char *get_cpu_str(void);

diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 39fec04..711f049 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -8,6 +8,7 @@ struct pmu_event {
const char *name;
const char *event;
const char *desc;
+ const char *long_desc;
};

/*
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 65f7572..819e7a5 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1521,7 +1521,7 @@ out_enomem:
/*
* Print the help text for the event symbols:
*/
-void print_events(const char *event_glob, bool name_only, bool quiet_flag)
+void print_events(const char *event_glob, bool name_only, bool quiet_flag, bool long_desc)
{
print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
@@ -1531,7 +1531,7 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag)

print_hwcache_events(event_glob, name_only);

- print_pmu_events(event_glob, name_only, quiet_flag);
+ print_pmu_events(event_glob, name_only, quiet_flag, long_desc);

if (event_glob != NULL)
return;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index d11f854..d0b640d 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -133,7 +133,7 @@ void parse_events_update_lists(struct list_head *list_event,
void parse_events_evlist_error(struct parse_events_evlist *data,
int idx, const char *str);

-void print_events(const char *event_glob, bool name_only, bool quiet);
+void print_events(const char *event_glob, bool name_only, bool quiet, bool long_desc);

struct event_symbol {
const char *symbol;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 2cf03a7..96bf8c7 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -208,7 +208,8 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
return 0;
}

-static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir, char *desc __maybe_unused, char *val)
+static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir, char *desc __maybe_unused, char *val,
+ char *long_desc)
{
struct perf_pmu_alias *alias;
int ret;
@@ -241,6 +242,9 @@ static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir,
}

alias->desc = desc ? strdup(desc) : NULL;
+ alias->long_desc = long_desc ?
+ strdup(long_desc) :
+ desc ? strdup(desc) : NULL;

list_add_tail(&alias->list, list);

@@ -257,7 +261,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
return -EINVAL;
buf[ret] = 0;

- return __perf_pmu__new_alias(list, name, dir, NULL, buf);
+ return __perf_pmu__new_alias(list, name, dir, NULL, buf, NULL);
}

static inline bool pmu_alias_info_file(char *name)
@@ -513,7 +517,8 @@ static int pmu_add_cpu_aliases(void *data)

/* need type casts to override 'const' */
__perf_pmu__new_alias(head, (char *)pe->name, NULL,
- (char *)pe->desc, (char *)pe->event);
+ (char *)pe->desc, (char *)pe->event,
+ (char *)pe->long_desc);
}

out:
@@ -1035,7 +1040,8 @@ static void wordwrap(char *s, int start, int max, int corr)
}
}

-void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
+void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
+ bool long_desc)
{
struct perf_pmu *pmu;
struct perf_pmu_alias *alias;
@@ -1082,7 +1088,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
pmu, alias);
aliases[j].name = strdup(aliases[j].name);
/* failure harmless */
- aliases[j].desc = alias->desc;
+ aliases[j].desc = long_desc ? alias->long_desc :
+ alias->desc;
j++;
}
if (pmu->selectable) {
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 9966c1a..1c95477 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -39,6 +39,7 @@ struct perf_pmu_info {
struct perf_pmu_alias {
char *name;
char *desc;
+ char *long_desc;
struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1];
@@ -69,7 +70,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head);

struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);

-void print_pmu_events(const char *event_glob, bool name_only, bool quiet);
+void print_pmu_events(const char *event_glob, bool name_only, bool quiet,
+ bool long_desc);
bool pmu_have_event(const char *pname, const char *name);

int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
--
1.7.9.5

2015-06-02 17:15:06

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 12/14] perf, tools: Add support for event list topics

From: Andi Kleen <[email protected]>

Add support to group the output of perf list by the Topic field
in the JSON file.

Example output:

% perf list
...
Cache:
l1d.replacement
[L1D data line replacements]
l1d_pend_miss.pending
[L1D miss oustandings duration in cycles]
l1d_pend_miss.pending_cycles
[Cycles with L1D load Misses outstanding]
l2_l1d_wb_rqsts.all
[Not rejected writebacks from L1D to L2 cache lines in any state]
l2_l1d_wb_rqsts.hit_e
[Not rejected writebacks from L1D to L2 cache lines in E state]
l2_l1d_wb_rqsts.hit_m
[Not rejected writebacks from L1D to L2 cache lines in M state]

...
Pipeline:
arith.fpu_div
[Divide operations executed]
arith.fpu_div_active
[Cycles when divider is busy executing divide operations]
baclears.any
[Counts the total number when the front end is resteered, mainly when the BPU cannot provide a correct prediction and this is corrected by
other branch handling mechanisms at the front end]
br_inst_exec.all_branches
[Speculative and retired branches]
br_inst_exec.all_conditional
[Speculative and retired macro-conditional branches]
br_inst_exec.all_direct_jmp
[Speculative and retired macro-unconditional branches excluding calls and indirects]
br_inst_exec.all_direct_near_call
[Speculative and retired direct near calls]
br_inst_exec.all_indirect_jump_non_call_ret

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>
---
tools/perf/pmu-events/jevents.c | 16 +++++++++++-----
tools/perf/pmu-events/jevents.h | 3 ++-
tools/perf/pmu-events/pmu-events.h | 1 +
tools/perf/util/pmu.c | 34 ++++++++++++++++++++++++----------
tools/perf/util/pmu.h | 1 +
5 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index 8990d71..14707fb 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -203,7 +203,7 @@ static void print_events_table_prefix(FILE *fp, const char *tblname)
}

static int print_events_table_entry(void *data, char *name, char *event,
- char *desc, char *long_desc)
+ char *desc, char *long_desc, char *topic)
{
FILE *outfp = data;
/*
@@ -216,7 +216,9 @@ static int print_events_table_entry(void *data, char *name, char *event,
fprintf(outfp, "\t.event = \"%s\",\n", event);
fprintf(outfp, "\t.desc = \"%s\",\n", desc);
if (long_desc && long_desc[0])
- fprintf(outfp, "\t.long_desc = \"%s\"", long_desc);
+ fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc);
+ if (topic)
+ fprintf(outfp, "\t.topic = \"%s\",\n", topic);
fprintf(outfp, "},\n");

return 0;
@@ -236,7 +238,8 @@ static void print_events_table_suffix(FILE *outfp)

/* Call func with each event in the json file */
int json_events(const char *fn,
- int (*func)(void *data, char *name, char *event, char *desc, char *long_desc),
+ int (*func)(void *data, char *name, char *event, char *desc,
+ char *long_desc, char *topic),
void *data)
{
int err = -EIO;
@@ -255,7 +258,7 @@ int json_events(const char *fn,
tok = tokens + 1;
for (i = 0; i < tokens->size; i++) {
char *event = NULL, *desc = NULL, *name = NULL, *long_desc = NULL;
- char *extra_desc = NULL;
+ char *extra_desc = NULL, *topic = NULL;
struct msrmap *msr = NULL;
jsmntok_t *msrval = NULL;
jsmntok_t *precise = NULL;
@@ -294,6 +297,8 @@ int json_events(const char *fn,
!json_streq(map, val, "null")) {
addfield(map, &extra_desc, ". ",
" Spec update: ", val);
+ } else if (json_streq(map, field, "Topic")) {
+ addfield(map, &topic, "", "", val);
} else if (json_streq(map, field, "Data_LA") && nz) {
addfield(map, &extra_desc, ". ",
" Supports address when precise",
@@ -316,12 +321,13 @@ int json_events(const char *fn,
if (msr != NULL)
addfield(map, &event, ",", msr->pname, msrval);
fixname(name);
- err = func(data, name, event, desc, long_desc);
+ err = func(data, name, event, desc, long_desc, topic);
free(event);
free(desc);
free(name);
free(extra_desc);
free(long_desc);
+ free(topic);
if (err)
break;
tok += j;
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h
index 2168ad4..a556ab1 100644
--- a/tools/perf/pmu-events/jevents.h
+++ b/tools/perf/pmu-events/jevents.h
@@ -2,7 +2,8 @@
#define JEVENTS_H 1

int json_events(const char *fn,
- int (*func)(void *data, char *name, char *event, char *desc, char *long_desc),
+ int (*func)(void *data, char *name, char *event, char *desc,
+ char *long_desc, char *topic),
void *data);
char *get_cpu_str(void);

diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 711f049..6b69f4b 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -9,6 +9,7 @@ struct pmu_event {
const char *event;
const char *desc;
const char *long_desc;
+ const char *topic;
};

/*
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 96bf8c7..6611d80 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -209,7 +209,7 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
}

static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir, char *desc __maybe_unused, char *val,
- char *long_desc)
+ char *long_desc, char *topic)
{
struct perf_pmu_alias *alias;
int ret;
@@ -245,6 +245,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir,
alias->long_desc = long_desc ?
strdup(long_desc) :
desc ? strdup(desc) : NULL;
+ alias->topic = topic ? strdup(topic) : NULL;

list_add_tail(&alias->list, list);

@@ -261,7 +262,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
return -EINVAL;
buf[ret] = 0;

- return __perf_pmu__new_alias(list, name, dir, NULL, buf, NULL);
+ return __perf_pmu__new_alias(list, name, dir, NULL, buf, NULL, NULL);
}

static inline bool pmu_alias_info_file(char *name)
@@ -518,7 +519,8 @@ static int pmu_add_cpu_aliases(void *data)
/* need type casts to override 'const' */
__perf_pmu__new_alias(head, (char *)pe->name, NULL,
(char *)pe->desc, (char *)pe->event,
- (char *)pe->long_desc);
+ (char *)pe->long_desc,
+ (char *)pe->topic);
}

out:
@@ -1002,19 +1004,25 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu,
return buf;
}

-struct pair {
+struct sevent {
char *name;
char *desc;
+ char *topic;
};

-static int cmp_pair(const void *a, const void *b)
+static int cmp_sevent(const void *a, const void *b)
{
- const struct pair *as = a;
- const struct pair *bs = b;
+ const struct sevent *as = a;
+ const struct sevent *bs = b;

/* Put extra events last */
if (!!as->desc != !!bs->desc)
return !!as->desc - !!bs->desc;
+ if (as->topic && bs->topic) {
+ int n = strcmp(as->topic, bs->topic);
+ if (n)
+ return n;
+ }
return strcmp(as->name, bs->name);
}

@@ -1048,11 +1056,12 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
char buf[1024];
int printed = 0;
int len, j;
- struct pair *aliases;
+ struct sevent *aliases;
int numdesc = 0;
int columns = pager_get_columns();
const char *last_section;
int last_section_len;
+ char *topic = NULL;

pmu = NULL;
len = 0;
@@ -1062,7 +1071,7 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
if (pmu->selectable)
len++;
}
- aliases = zalloc(sizeof(struct pair) * len);
+ aliases = zalloc(sizeof(struct sevent) * len);
if (!aliases)
goto out_enomem;
pmu = NULL;
@@ -1090,6 +1099,7 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
/* failure harmless */
aliases[j].desc = long_desc ? alias->long_desc :
alias->desc;
+ aliases[j].topic = alias->topic;
j++;
}
if (pmu->selectable) {
@@ -1103,7 +1113,7 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
len = j;
last_section = NULL;
last_section_len = 0;
- qsort(aliases, len, sizeof(struct pair), cmp_pair);
+ qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
for (j = 0; j < len; j++) {
if (name_only) {
printf("%s ", aliases[j].name);
@@ -1115,6 +1125,10 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,

if (numdesc++ == 0)
printf("\n");
+ if (aliases[j].topic && (!topic || strcmp(topic, aliases[j].topic))) {
+ printf("%s%s:\n", topic ? "\n" : "", aliases[j].topic);
+ topic = aliases[j].topic;
+ }
dot = strchr(name, '.');
if (dot &&
(!last_section ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 1c95477..2d0aab3 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -40,6 +40,7 @@ struct perf_pmu_alias {
char *name;
char *desc;
char *long_desc;
+ char *topic;
struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1];
--
1.7.9.5

2015-06-02 17:14:44

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 13/14] perf, tools: Handle header line in mapfile

From: Andi Kleen <[email protected]>

Support a header line in the mapfile.csv, to match the existing
mapfiles

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

Changelog[v2]
All architectures may not use the "Family" to identify. So,
assume first line is header.
---
tools/perf/pmu-events/jevents.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index 14707fb..8d365f2 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -461,7 +461,12 @@ static int process_mapfile(FILE *outfp, char *fpath)

print_mapping_table_prefix(outfp);

- line_num = 0;
+ /* Skip first line (header) */
+ p = fgets(line, n, mapfp);
+ if (!p)
+ goto out;
+
+ line_num = 1;
while (1) {
char *cpuid, *version, *type, *fname;

@@ -505,8 +510,8 @@ static int process_mapfile(FILE *outfp, char *fpath)
fprintf(outfp, "},\n");
}

+out:
print_mapping_table_suffix(outfp);
-
return 0;
}

--
1.7.9.5

2015-06-02 17:14:35

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: [PATCH v13 14/14] perf, tools: Add README for the JSON/map files parsing

Signed-off-by: Sukadev Bhattiprolu <[email protected]>
---
tools/perf/pmu-events/README | 115 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
create mode 100644 tools/perf/pmu-events/README

diff --git a/tools/perf/pmu-events/README b/tools/perf/pmu-events/README
new file mode 100644
index 0000000..00fd2e1
--- /dev/null
+++ b/tools/perf/pmu-events/README
@@ -0,0 +1,115 @@
+The contents of this directory allows users to specify PMU events in
+their CPUs by their symbolic names rather than raw event codes (see
+example below).
+
+The main program in this directory, is the 'jevents', which is built and
+executed _before_ the perf binary itself is built.
+
+The 'jevents' program tries to locate and process JSON files in the directory
+tree tools/perf/pmu-events/arch/xxx.
+
+ - Regular files with '.json' extension in the name are assumed to be
+ JSON files.
+
+ - Regular files with base name starting with 'mapfile.csv' are assumed
+ to be a CSV file that - maps a specific CPU to its set of PMU events.
+
+Directories are traversed, but all other files are ignored.
+
+Using the JSON files and the mapfile, 'jevents' generates a C source file,
+'pmu-events.c', which encodes the two sets of tables:
+
+ - Set of 'PMU events tables' for all known CPUs in the architecture,
+ (one table like the following, per JSON file; table name 'pme_power8'
+ is derived from JSON file name, 'power8.json').
+
+ struct pmu_event pme_power8[] = {
+
+ ...
+
+ {
+ .name = "pm_1plus_ppc_cmpl",
+ .event = "event=0x100f2",
+ .desc = "1 or more ppc insts finished,",
+ },
+
+ ...
+ }
+
+ - A 'mapping table' that maps each CPU of the architecture, to its
+ 'PMU events table'
+
+ struct pmu_events_map pmu_events_map[] = {
+ {
+ .cpuid = "004b0000",
+ .version = "1",
+ .type = "core",
+ .table = pme_power8
+ },
+ {
+ ...
+ },
+
+ };
+
+After the 'pmu-events.c' is generated, it is compiled and the resulting
+'pmu-events.o' is added to 'libperf.a' which is then used to build perf.
+
+NOTES:
+ 1. Several CPUs can support same set of events and hence use a common
+ JSON file. Hence several entries in the pmu_events_map[] could map
+ to a single 'PMU events table'.
+
+ 2. The 'pmu-events.h' has an extern declaration for the mapping table
+ and the generated 'pmu-events.c' defines this table.
+
+ 3. _All_ known CPU tables for architecture are included in the perf
+ binary.
+
+At run time, perf determines the actual CPU it is running on, finds the
+matching events table and builds aliases for those events. This allows
+users to specify events by their name:
+
+ $ perf stat -e pm_1plus_ppc_cmpl sleep 1
+
+where 'pm_1plus_ppc_cmpl' is a Power8 PMU event.
+
+In case of errors when processing files in the tools/perf/pmu-events/arch
+directory, 'jevents' tries to create an empty mapping file to allow perf
+build to succeed even if the PMU event aliases cannot be used.
+
+However some errors in processing may cause the perf build to fail.
+
+The mapfile format is expected to be:
+
+ Header line
+ CPUID,Version,JSON/file/path/name,Type
+
+where:
+
+ Lines in which the first character is either '\n' or '#' are ignored.
+
+ Header line
+ is the first line in the file and is ignored. Even if
+ first line is empty or is a comment, subsequent non-comment/
+ blank lines are expected to adhere to the format of the second
+ line above.
+
+ Comma:
+ is the required field delimiter (i.e other fields cannot
+ have commas within them)
+
+ CPUID:
+ represents is an arch-specific CPUID for the set of CPUs
+ that use the PMU events specified in JSON/file/path/name.
+ (Multiple CPU ids can point to the same JSON/file/path/name)
+
+ Version:
+ is the Version of the mapfile.
+
+ JSON/file/path/name:
+ is the pathname for the JSON file, relative to the directory
+ containing the mapfile.csv
+
+ Type:
+ indicates whether the events or "core" or "uncore" events.
--
1.7.9.5

2015-06-02 19:17:31

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: Re: [PATCH v13 12/14] perf, tools: Add support for event list topics

Sukadev Bhattiprolu [[email protected]] wrote:
| From: Andi Kleen <[email protected]>
|
| Add support to group the output of perf list by the Topic field
| in the JSON file.

[PATCH 9/14] has been dropped from this set. This alters the current,
patch [PATCH 12/14], slightly, which is included below.

The branch 'json-v13.1' on github has the updated set of patches:

https://github.com/sukadev/linux/tree/json-v13.1

>From 0d8fe0d46423ee1e2dcf7d92014b693a3ab14086 Mon Sep 17 00:00:00 2001
From: Andi Kleen <[email protected]>
Date: Sun, 31 May 2015 21:19:20 -0400
Subject: [PATCH 12/14] perf, tools: Add support for event list topics

Add support to group the output of perf list by the Topic field
in the JSON file.

Example output:

% perf list
...
Cache:
l1d.replacement
[L1D data line replacements]
l1d_pend_miss.pending
[L1D miss oustandings duration in cycles]
l1d_pend_miss.pending_cycles
[Cycles with L1D load Misses outstanding]
l2_l1d_wb_rqsts.all
[Not rejected writebacks from L1D to L2 cache lines in any state]
l2_l1d_wb_rqsts.hit_e
[Not rejected writebacks from L1D to L2 cache lines in E state]
l2_l1d_wb_rqsts.hit_m
[Not rejected writebacks from L1D to L2 cache lines in M state]

...
Pipeline:
arith.fpu_div
[Divide operations executed]
arith.fpu_div_active
[Cycles when divider is busy executing divide operations]
baclears.any
[Counts the total number when the front end is resteered, mainly when the BPU cannot provide a correct prediction and this is corrected by
other branch handling mechanisms at the front end]
br_inst_exec.all_branches
[Speculative and retired branches]
br_inst_exec.all_conditional
[Speculative and retired macro-conditional branches]
br_inst_exec.all_direct_jmp
[Speculative and retired macro-unconditional branches excluding calls and indirects]
br_inst_exec.all_direct_near_call
[Speculative and retired direct near calls]
br_inst_exec.all_indirect_jump_non_call_ret

Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Sukadev Bhattiprolu <[email protected]>

Changelog[v2]
Dropped an unnecessary patch before this and fixed resulting
conflicts in tools/perf/util/pmu.c
---
tools/perf/pmu-events/jevents.c | 16 +++++++++++-----
tools/perf/pmu-events/jevents.h | 3 ++-
tools/perf/pmu-events/pmu-events.h | 1 +
tools/perf/util/pmu.c | 34 ++++++++++++++++++++++++----------
tools/perf/util/pmu.h | 1 +
5 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index 8990d71..14707fb 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -203,7 +203,7 @@ static void print_events_table_prefix(FILE *fp, const char *tblname)
}

static int print_events_table_entry(void *data, char *name, char *event,
- char *desc, char *long_desc)
+ char *desc, char *long_desc, char *topic)
{
FILE *outfp = data;
/*
@@ -216,7 +216,9 @@ static int print_events_table_entry(void *data, char *name, char *event,
fprintf(outfp, "\t.event = \"%s\",\n", event);
fprintf(outfp, "\t.desc = \"%s\",\n", desc);
if (long_desc && long_desc[0])
- fprintf(outfp, "\t.long_desc = \"%s\"", long_desc);
+ fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc);
+ if (topic)
+ fprintf(outfp, "\t.topic = \"%s\",\n", topic);
fprintf(outfp, "},\n");

return 0;
@@ -236,7 +238,8 @@ static void print_events_table_suffix(FILE *outfp)

/* Call func with each event in the json file */
int json_events(const char *fn,
- int (*func)(void *data, char *name, char *event, char *desc, char *long_desc),
+ int (*func)(void *data, char *name, char *event, char *desc,
+ char *long_desc, char *topic),
void *data)
{
int err = -EIO;
@@ -255,7 +258,7 @@ int json_events(const char *fn,
tok = tokens + 1;
for (i = 0; i < tokens->size; i++) {
char *event = NULL, *desc = NULL, *name = NULL, *long_desc = NULL;
- char *extra_desc = NULL;
+ char *extra_desc = NULL, *topic = NULL;
struct msrmap *msr = NULL;
jsmntok_t *msrval = NULL;
jsmntok_t *precise = NULL;
@@ -294,6 +297,8 @@ int json_events(const char *fn,
!json_streq(map, val, "null")) {
addfield(map, &extra_desc, ". ",
" Spec update: ", val);
+ } else if (json_streq(map, field, "Topic")) {
+ addfield(map, &topic, "", "", val);
} else if (json_streq(map, field, "Data_LA") && nz) {
addfield(map, &extra_desc, ". ",
" Supports address when precise",
@@ -316,12 +321,13 @@ int json_events(const char *fn,
if (msr != NULL)
addfield(map, &event, ",", msr->pname, msrval);
fixname(name);
- err = func(data, name, event, desc, long_desc);
+ err = func(data, name, event, desc, long_desc, topic);
free(event);
free(desc);
free(name);
free(extra_desc);
free(long_desc);
+ free(topic);
if (err)
break;
tok += j;
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h
index 2168ad4..a556ab1 100644
--- a/tools/perf/pmu-events/jevents.h
+++ b/tools/perf/pmu-events/jevents.h
@@ -2,7 +2,8 @@
#define JEVENTS_H 1

int json_events(const char *fn,
- int (*func)(void *data, char *name, char *event, char *desc, char *long_desc),
+ int (*func)(void *data, char *name, char *event, char *desc,
+ char *long_desc, char *topic),
void *data);
char *get_cpu_str(void);

diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index 711f049..6b69f4b 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -9,6 +9,7 @@ struct pmu_event {
const char *event;
const char *desc;
const char *long_desc;
+ const char *topic;
};

/*
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index a4e49c5..8170dce 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -209,7 +209,7 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
}

static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir, char *desc __maybe_unused, char *val,
- char *long_desc)
+ char *long_desc, char *topic)
{
struct perf_pmu_alias *alias;
int ret;
@@ -245,6 +245,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *name, char *dir,
alias->long_desc = long_desc ?
strdup(long_desc) :
desc ? strdup(desc) : NULL;
+ alias->topic = topic ? strdup(topic) : NULL;

list_add_tail(&alias->list, list);

@@ -261,7 +262,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
return -EINVAL;
buf[ret] = 0;

- return __perf_pmu__new_alias(list, name, dir, NULL, buf, NULL);
+ return __perf_pmu__new_alias(list, name, dir, NULL, buf, NULL, NULL);
}

static inline bool pmu_alias_info_file(char *name)
@@ -518,7 +519,8 @@ static int pmu_add_cpu_aliases(void *data)
/* need type casts to override 'const' */
__perf_pmu__new_alias(head, (char *)pe->name, NULL,
(char *)pe->desc, (char *)pe->event,
- (char *)pe->long_desc);
+ (char *)pe->long_desc,
+ (char *)pe->topic);
}

out:
@@ -1002,19 +1004,25 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu,
return buf;
}

-struct pair {
+struct sevent {
char *name;
char *desc;
+ char *topic;
};

-static int cmp_pair(const void *a, const void *b)
+static int cmp_sevent(const void *a, const void *b)
{
- const struct pair *as = a;
- const struct pair *bs = b;
+ const struct sevent *as = a;
+ const struct sevent *bs = b;

/* Put extra events last */
if (!!as->desc != !!bs->desc)
return !!as->desc - !!bs->desc;
+ if (as->topic && bs->topic) {
+ int n = strcmp(as->topic, bs->topic);
+ if (n)
+ return n;
+ }
return strcmp(as->name, bs->name);
}

@@ -1048,9 +1056,10 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
char buf[1024];
int printed = 0;
int len, j;
- struct pair *aliases;
+ struct sevent *aliases;
int numdesc = 0;
int columns = pager_get_columns();
+ char *topic = NULL;

pmu = NULL;
len = 0;
@@ -1060,7 +1069,7 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
if (pmu->selectable)
len++;
}
- aliases = zalloc(sizeof(struct pair) * len);
+ aliases = zalloc(sizeof(struct sevent) * len);
if (!aliases)
goto out_enomem;
pmu = NULL;
@@ -1088,6 +1097,7 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
/* failure harmless */
aliases[j].desc = long_desc ? alias->long_desc :
alias->desc;
+ aliases[j].topic = alias->topic;
j++;
}
if (pmu->selectable) {
@@ -1099,7 +1109,7 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
}
}
len = j;
- qsort(aliases, len, sizeof(struct pair), cmp_pair);
+ qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
for (j = 0; j < len; j++) {
if (name_only) {
printf("%s ", aliases[j].name);
@@ -1108,6 +1118,10 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
if (aliases[j].desc && !quiet_flag) {
if (numdesc++ == 0)
printf("\n");
+ if (aliases[j].topic && (!topic || strcmp(topic, aliases[j].topic))) {
+ printf("%s%s:\n", topic ? "\n" : "", aliases[j].topic);
+ topic = aliases[j].topic;
+ }
printf(" %-50s\n", aliases[j].name);
printf("%*s", 8, "[");
wordwrap(aliases[j].desc, 8, columns, 0);
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 1c95477..2d0aab3 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -40,6 +40,7 @@ struct perf_pmu_alias {
char *name;
char *desc;
char *long_desc;
+ char *topic;
struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1];
--
1.8.3.1

2015-06-02 19:20:52

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: Re: [PATCH v13 09/14] perf, tools: Group alias perf list by section


Please drop this patch. It was incorrectly included in the set.
Also, see the updated Patch 12/14.

Current set of patches are in the json-v13.1 branch:

https://github.com/sukadev/linux/tree/json-v13.1

Sukadev

Sukadev Bhattiprolu [[email protected]] wrote:
| From: Andi Kleen <[email protected]>
|
| The first first element in an hardware event name defines the general
| area, usually the part of the micro architecture it is refering to.
| Group the perf list output by these sections by adding section headers.
|
| Signed-off-by: Andi Kleen <[email protected]>
| Signed-off-by: Sukadev Bhattiprolu <[email protected]>
| ---
| tools/perf/util/pmu.c | 17 +++++++++++++++++
| 1 file changed, 17 insertions(+)
|
| diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
| index 417333b..8d91fa7 100644
| --- a/tools/perf/util/pmu.c
| +++ b/tools/perf/util/pmu.c
| @@ -1039,6 +1039,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
| struct pair *aliases;
| int numdesc = 0;
| int columns = pager_get_columns();
| + const char *last_section;
| + int last_section_len;
|
| pmu = NULL;
| len = 0;
| @@ -1086,6 +1088,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
| }
| }
| len = j;
| + last_section = NULL;
| + last_section_len = 0;
| qsort(aliases, len, sizeof(struct pair), cmp_pair);
| for (j = 0; j < len; j++) {
| if (name_only) {
| @@ -1093,8 +1097,21 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag)
| continue;
| }
| if (aliases[j].desc && !quiet_flag) {
| + char *dot;
| + char *name = aliases[j].name;
| +
| if (numdesc++ == 0)
| printf("\n");
| + dot = strchr(name, '.');
| + if (dot &&
| + (!last_section ||
| + strncmp(last_section, name, last_section_len))) {
| + last_section_len = dot - name;
| + printf("%s%.*s:\n",
| + last_section ? "\n" : "",
| + last_section_len, name);
| + last_section = name;
| + }
| printf(" %-50s\n", aliases[j].name);
| printf("%*s", 8, "[");
| wordwrap(aliases[j].desc, 8, columns, 0);
| --
| 1.7.9.5
|
| --
| To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
| the body of a message to [email protected]
| More majordomo info at http://vger.kernel.org/majordomo-info.html
| Please read the FAQ at http://www.tux.org/lkml/

2015-06-02 19:23:19

by Sukadev Bhattiprolu

[permalink] [raw]
Subject: Re: [PATCH V13 00/14] perf, tools: Add support for PMU events in JSON format

Sukadev Bhattiprolu [[email protected]] wrote:
|
| These patches are available from:
|
| https://github.com:sukadev/linux.git
|
| Branch Description
| ------------------------------------------------------
| json-v13 Source Code only
| json-files-3 x86 and Powerpc datafiles only
| json-v13-with-data Both code and data (build/test)

Please use following branches instead:

json-v13.1 Source Code only
json-files-3 x86 and Powerpc datafiles only
json-v13.1-with-data Both code and data (build/test)

2015-06-03 10:30:47

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 03/14] perf, tools: Use pmu_events_map table to create event aliases

On Tue, Jun 02, 2015 at 10:12:03AM -0700, Sukadev Bhattiprolu wrote:

SNIP

> +
> +/*
> + * From the pmu_events_map, find the table of PMU events that corresponds
> + * to the current running CPU. Then, add all PMU events from that table
> + * as aliases.
> + */
> +static int pmu_add_cpu_aliases(void *data)

any reason why the argument is not 'head' directly?

jirka

> +{
> + struct list_head *head = (struct list_head *)data;
> + int i;
> + struct pmu_events_map *map;
> + struct pmu_event *pe;
> + char *cpuid;
> +
> + cpuid = get_cpuid_str();
> + if (!cpuid)
> + return 0;
> +
> + i = 0;
> + while (1) {
> + map = &pmu_events_map[i++];
> + if (!map->table) {
> + goto out;
> + }
> +
> + if (!strcmp(map->cpuid, cpuid))
> + break;
> + }
> +
> + /*
> + * Found a matching PMU events table. Create aliases
> + */
> + i = 0;
> + while (1) {
> + pe = &map->table[i++];
> + if (!pe->name)
> + break;
> +
> + /* need type casts to override 'const' */
> + __perf_pmu__new_alias(head, (char *)pe->name, NULL,
> + (char *)pe->desc, (char *)pe->event);
> + }
> +
> +out:
> + free(cpuid);
> + return 0;
> +}
> +
> +
> static struct perf_pmu *pmu_lookup(const char *name)
> {
> struct perf_pmu *pmu;
> @@ -464,6 +540,8 @@ static struct perf_pmu *pmu_lookup(const char *name)
> if (pmu_aliases(name, &aliases))
> return NULL;
>
> + if (!strcmp(name, "cpu"))
> + (void)pmu_add_cpu_aliases(&aliases);
> if (pmu_type(name, &type))
> return NULL;
>
> --
> 1.7.9.5
>

2015-06-03 10:30:59

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 02/14] perf, tools, jevents: Program to convert JSON file to C style file

On Tue, Jun 02, 2015 at 10:12:02AM -0700, Sukadev Bhattiprolu wrote:

SNIP

> +
> +static char *file_name_to_table_name(char *fname)
> +{
> + unsigned int i, j;
> + int c;
> + int n = 1024; /* use max variable length? */

I think this should be at least PATH_MAX, or you might
actually use asprintf and have all below done within
one line or so

jirka

> + char *tblname;
> + char *p;
> +
> + tblname = malloc(n);
> + if (!tblname)
> + return NULL;
> +
> + p = basename(fname);
> +
> + memset(tblname, 0, n);
> +
> + /* Ensure table name starts with an alphabetic char */
> + strcpy(tblname, "pme_");
> +
> + n = strlen(fname) + strlen(tblname);
> + n = min(1024, n);
> +
> + for (i = 0, j = strlen(tblname); i < strlen(fname); i++, j++) {
> + c = p[i];
> + if (isalnum(c) || c == '_')
> + tblname[j] = c;
> + else if (c == '-')
> + tblname[j] = '_';
> + else if (c == '.') {
> + tblname[j] = '\0';
> + break;
> + } else {
> + pr_err("%s: Invalid character '%c' in file name %s\n",
> + prog, c, p);
> + free(tblname);
> + return NULL;
> + }
> + }
> +
> + return tblname;
> +}

SNIP

2015-06-03 10:31:14

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 03/14] perf, tools: Use pmu_events_map table to create event aliases

On Tue, Jun 02, 2015 at 10:12:03AM -0700, Sukadev Bhattiprolu wrote:

SNIP

>
> +/*
> + * Return the CPU id as a raw string.
> + *
> + * Each architecture should provide a more precise id string that
> + * can be use to match the architecture's "mapfile".
> + */
> +char *__attribute__((weak))get_cpuid_str(void)

we have '__weak' define in <linux/compiler.h> include

jirka

2015-06-03 10:31:20

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 03/14] perf, tools: Use pmu_events_map table to create event aliases

On Tue, Jun 02, 2015 at 10:12:03AM -0700, Sukadev Bhattiprolu wrote:
> At run time, (i.e when perf is starting up), locate the specific events
> table for the current CPU and create event aliases for each of the events.
>
> Use these aliases to parse user's specified perf event.
>
> Signed-off-by: Sukadev Bhattiprolu <[email protected]>
>
> Changelog[v3]
> [Jiri Olsa] Fix a memory leak with cpuid.
>
> Changelog[v2]
> [Andi Kleen] Replace the pmu_events_map->vfm field with a simple
> generic "cpuid" string and use that string to find the
> matching mapfile entry.
> ---
> tools/perf/arch/powerpc/util/header.c | 11 ++++
> tools/perf/util/header.h | 3 +-
> tools/perf/util/pmu.c | 104 ++++++++++++++++++++++++++++-----
> 3 files changed, 104 insertions(+), 14 deletions(-)

I think this patch is doing too much, it should be split into 3 pieces:

- introduce get_cpuid_str for powerpc
- introducing __perf_pmu__new_alias/perf_pmu__new_alias functions split
- adding pmu_add_cpu_aliases functionality

jirka

2015-06-03 10:31:52

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 02/14] perf, tools, jevents: Program to convert JSON file to C style file

On Tue, Jun 02, 2015 at 10:12:02AM -0700, Sukadev Bhattiprolu wrote:

SNIP

> + * If we fail to locate/process JSON and map files, create a NULL mapping
> + * table. This would at least allow perf to build even if we can't find/use
> + * the aliases.
> + */
> +static void create_empty_mapping(const char *output_file)
> +{
> + FILE *outfp;
> +
> + pr_info("%s: Creating empty pmu_events_map[] table\n", prog);
> +
> + /* Unlink file to clear any partial writes to it */
> + unlink(output_file);
> +
> + outfp = fopen(output_file, "a");

you could open with "w+" and save the unlink call

SNIP

> +int main(int argc, char *argv[])
> +{
> + int rc;
> + int flags;
> + int maxfds;
> + char dirname[PATH_MAX];
> +
> + const char *arch;
> + const char *output_file;
> + const char *start_dirname;
> +
> + prog = basename(argv[0]);
> + if (argc < 4) {
> + pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog);
> + return 1;
> + }
> +
> + arch = argv[1];
> + start_dirname = argv[2];
> + output_file = argv[3];
> +
> + if (argc > 4)
> + verbose = atoi(argv[4]);
> +
> + unlink(output_file);
> + eventsfp = fopen(output_file, "a");

ditto

SNIP

2015-06-03 10:31:59

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 03/14] perf, tools: Use pmu_events_map table to create event aliases

On Tue, Jun 02, 2015 at 10:12:03AM -0700, Sukadev Bhattiprolu wrote:

SNIP

> @@ -225,26 +221,47 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
> alias->unit[0] = '\0';
> alias->per_pkg = false;
>
> - ret = parse_events_terms(&alias->terms, buf);
> + ret = parse_events_terms(&alias->terms, val);
> if (ret) {
> + pr_err("Cannot parse alias %s: %d\n", val, ret);
> free(alias);
> return ret;
> }
>
> alias->name = strdup(name);
> + if (dir) {
> + /*
> + * load unit name and scale if available
> + */
> + perf_pmu__parse_unit(alias, dir, name);
> + perf_pmu__parse_scale(alias, dir, name);
> + perf_pmu__parse_per_pkg(alias, dir, name);
> + perf_pmu__parse_snapshot(alias, dir, name);
> + }
> +
> /*
> - * load unit name and scale if available
> + * TODO: pickup description from Andi's patchset
> */
> - perf_pmu__parse_unit(alias, dir, name);
> - perf_pmu__parse_scale(alias, dir, name);
> - perf_pmu__parse_per_pkg(alias, dir, name);
> - perf_pmu__parse_snapshot(alias, dir, name);
> + //alias->desc = desc ? strdpu(desc) : NULL;

please remove the TODO line and above commented code,
it is addressed later in this patchset

jirka

2015-06-03 10:32:04

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 06/14] perf, tools: Support alias descriptions

On Tue, Jun 02, 2015 at 10:12:06AM -0700, Sukadev Bhattiprolu wrote:

SNIP

> @@ -1033,37 +1064,49 @@ void print_pmu_events(const char *event_glob, bool name_only)
> event_glob))))
> continue;
>
> - if (is_cpu && !name_only)
> + if (is_cpu && !name_only && !alias->desc)
> name = format_alias_or(buf, sizeof(buf), pmu, alias);
>
> - aliases[j] = strdup(name);
> - if (aliases[j] == NULL)
> - goto out_enomem;
> + aliases[j].name = name;
> + if (is_cpu && !name_only && !alias->desc)
> + aliases[j].name = format_alias_or(buf, sizeof(buf),
> + pmu, alias);
> + aliases[j].name = strdup(aliases[j].name);
> + /* failure harmless */

yea but we still try to care everywhere.. ;-)
we would print "NULL" for name in the code below right?

please keep the above pattern:

if (aliases[j].name == NULL)
goto out_enomem;



> + aliases[j].desc = alias->desc;
> j++;
> }
> if (pmu->selectable) {
> char *s;
> if (asprintf(&s, "%s//", pmu->name) < 0)
> goto out_enomem;
> - aliases[j] = s;
> + aliases[j].name = s;
> j++;
> }
> }
> len = j;
> - qsort(aliases, len, sizeof(char *), cmp_string);
> + qsort(aliases, len, sizeof(struct pair), cmp_pair);
> for (j = 0; j < len; j++) {
> if (name_only) {
> - printf("%s ", aliases[j]);
> + printf("%s ", aliases[j].name);
> continue;
> }
> - printf(" %-50s [Kernel PMU event]\n", aliases[j]);
> + if (aliases[j].desc) {
> + if (numdesc++ == 0)
> + printf("\n");
> + printf(" %-50s\n", aliases[j].name);
> + printf("%*s", 8, "[");
> + wordwrap(aliases[j].desc, 8, columns, 0);
> + printf("]\n");
> + } else
> + printf(" %-50s [Kernel PMU event]\n", aliases[j].name);
> printed++;

SNIP

2015-06-03 10:32:22

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 04/14] perf, tools: Allow events with dot

On Tue, Jun 02, 2015 at 10:12:04AM -0700, Sukadev Bhattiprolu wrote:
> From: Andi Kleen <[email protected]>
>
> The Intel events use a dot to separate event name and unit mask.
> Allow dot in names in the scanner, and remove special handling
> of dot as EOF. Also remove the hack in jevents to replace dot
> with underscore. This way dotted events can be specified
> directly by the user.
>
> I'm not fully sure this change to the scanner is correct
> (what was the dot special case good for?), but I haven't
> found anything that breaks with it so far at least.

can't see anything either

Robert,
does it ring a bell? seems like you introduced it ;-)

thanks,
jirka


>
> V2: Add the dot to name too, to handle events outside cpu//
> Acked-by: Namhyung Kim <[email protected]>
> Signed-off-by: Andi Kleen <[email protected]>
> Signed-off-by: Sukadev Bhattiprolu <[email protected]>
> ---
> tools/perf/util/parse-events.l | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
> index 09e738f..13cef3c 100644
> --- a/tools/perf/util/parse-events.l
> +++ b/tools/perf/util/parse-events.l
> @@ -119,8 +119,8 @@ event [^,{}/]+
> num_dec [0-9]+
> num_hex 0x[a-fA-F0-9]+
> num_raw_hex [a-fA-F0-9]+
> -name [a-zA-Z_*?][a-zA-Z0-9_*?]*
> -name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]*
> +name [a-zA-Z_*?][a-zA-Z0-9_*?.]*
> +name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]*
> /* If you add a modifier you need to update check_modifier() */
> modifier_event [ukhpGHSDI]+
> modifier_bp [rwx]{1,3}
> @@ -165,7 +165,6 @@ modifier_bp [rwx]{1,3}
> return PE_EVENT_NAME;
> }
>
> -. |
> <<EOF>> {
> BEGIN(INITIAL);
> REWIND(0);
> --
> 1.7.9.5
>

2015-06-03 10:32:38

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 11/14] perf, tools: Support long descriptions with perf list -v

On Tue, Jun 02, 2015 at 10:12:11AM -0700, Sukadev Bhattiprolu wrote:
> From: Andi Kleen <[email protected]>
>
> Previously we were dropping the useful longer descriptions that some
> events have in the event list completely. This patch makes them appear with
> perf list.
>
> Old perf list:
>
> baclears:
> baclears.all
> [Counts the number of baclears]
>
> vs new:
>
> perf list -v:
> ...
> baclears:
> baclears.all
> [The BACLEARS event counts the number of times the front end is resteered, mainly when
> the Branch Prediction Unit cannot provide a correct prediction and this is corrected
> by the Branch Address Calculator at the front end. The BACLEARS.ANY event counts the
> number of baclears for any type of branch]
>
> Signed-off-by: Andi Kleen <[email protected]>
> Signed-off-by: Sukadev Bhattiprolu <[email protected]>
> ---
> tools/perf/builtin-list.c | 9 ++++++---
> tools/perf/pmu-events/jevents.c | 29 ++++++++++++++++++++---------
> tools/perf/pmu-events/jevents.h | 2 +-
> tools/perf/pmu-events/pmu-events.h | 1 +
> tools/perf/util/parse-events.c | 4 ++--
> tools/perf/util/parse-events.h | 2 +-
> tools/perf/util/pmu.c | 17 ++++++++++++-----
> tools/perf/util/pmu.h | 4 +++-
> 8 files changed, 46 insertions(+), 22 deletions(-)

I think this change should be split into:
- jevents update of parsing out PublicDescription tag
- alias support for long_desc
- perf list update

jirka

2015-06-03 10:32:52

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 12/14] perf, tools: Add support for event list topics

On Tue, Jun 02, 2015 at 12:16:41PM -0700, Sukadev Bhattiprolu wrote:

SNIP

> [Speculative and retired macro-conditional branches]
> br_inst_exec.all_direct_jmp
> [Speculative and retired macro-unconditional branches excluding calls and indirects]
> br_inst_exec.all_direct_near_call
> [Speculative and retired direct near calls]
> br_inst_exec.all_indirect_jump_non_call_ret
>
> Signed-off-by: Andi Kleen <[email protected]>
> Signed-off-by: Sukadev Bhattiprolu <[email protected]>
>
> Changelog[v2]
> Dropped an unnecessary patch before this and fixed resulting
> conflicts in tools/perf/util/pmu.c
> ---
> tools/perf/pmu-events/jevents.c | 16 +++++++++++-----
> tools/perf/pmu-events/jevents.h | 3 ++-
> tools/perf/pmu-events/pmu-events.h | 1 +
> tools/perf/util/pmu.c | 34 ++++++++++++++++++++++++----------
> tools/perf/util/pmu.h | 1 +
> 5 files changed, 39 insertions(+), 16 deletions(-)

please split at least the jevents Topic parsing from the rest
idelay also the alias update and the display change

jirka

2015-06-03 10:33:03

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 13/14] perf, tools: Handle header line in mapfile

On Tue, Jun 02, 2015 at 10:12:13AM -0700, Sukadev Bhattiprolu wrote:
> From: Andi Kleen <[email protected]>
>
> Support a header line in the mapfile.csv, to match the existing
> mapfiles

'Suport' means 'skip' in here

jirka

>
> Signed-off-by: Andi Kleen <[email protected]>
> Signed-off-by: Sukadev Bhattiprolu <[email protected]>
>
> Changelog[v2]
> All architectures may not use the "Family" to identify. So,
> assume first line is header.
> ---
> tools/perf/pmu-events/jevents.c | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
> index 14707fb..8d365f2 100644
> --- a/tools/perf/pmu-events/jevents.c
> +++ b/tools/perf/pmu-events/jevents.c
> @@ -461,7 +461,12 @@ static int process_mapfile(FILE *outfp, char *fpath)
>
> print_mapping_table_prefix(outfp);
>
> - line_num = 0;
> + /* Skip first line (header) */
> + p = fgets(line, n, mapfp);
> + if (!p)
> + goto out;
> +
> + line_num = 1;
> while (1) {
> char *cpuid, *version, *type, *fname;
>
> @@ -505,8 +510,8 @@ static int process_mapfile(FILE *outfp, char *fpath)
> fprintf(outfp, "},\n");
> }
>
> +out:
> print_mapping_table_suffix(outfp);
> -
> return 0;
> }
>
> --
> 1.7.9.5
>

2015-06-03 10:33:10

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 02/14] perf, tools, jevents: Program to convert JSON file to C style file

On Tue, Jun 02, 2015 at 10:12:02AM -0700, Sukadev Bhattiprolu wrote:

SNIP

> +
> +static int process_mapfile(FILE *outfp, char *fpath)
> +{
> + int n = 16384;
> + FILE *mapfp;
> + char *save;
> + char *line, *p;
> + int line_num;
> + char *tblname;
> +
> + pr_info("%s: Processing mapfile %s\n", prog, fpath);

SNIP

> +
> + cpuid = strtok_r(p, ",", &save);
> + version = strtok_r(NULL, ",", &save);
> + fname = strtok_r(NULL, ",", &save);
> + type = strtok_r(NULL, ",", &save);
> +
> + tblname = file_name_to_table_name(fname);
> + fprintf(outfp, "{\n");
> + fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid);
> + fprintf(outfp, "\t.version = \"%s\",\n", version);
> + fprintf(outfp, "\t.type = \"%s\",\n", type);

got build failure for make DEBUG=1:

CC pmu-events/jevents.o
pmu-events/jevents.c: In function ‘process_mapfile’:
pmu-events/jevents.c:498:10: error: ‘save’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
fprintf(outfp, "\t.type = \"%s\",\n", type);
^

jirka

2015-06-03 12:58:06

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH v13 12/14] perf, tools: Add support for event list topics

> please split at least the jevents Topic parsing from the rest
> idelay also the alias update and the display change

What's the point of all these splits? It's already one logical unit,
not too large, and is bisectable.

-andi

--
[email protected] -- Speaking for myself only

2015-06-03 13:26:57

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH v13 12/14] perf, tools: Add support for event list topics

Em Wed, Jun 03, 2015 at 05:57:33AM -0700, Andi Kleen escreveu:
> > please split at least the jevents Topic parsing from the rest
> > idelay also the alias update and the display change
>
> What's the point of all these splits? It's already one logical unit,
> not too large, and is bisectable.

Eases review, improves bisectability, and its a reasonable request from
a reviewer/maintainer that has to look at an evergrowing number of patch
flows.

- Arnaldo

2015-06-03 14:25:35

by Jiri Olsa

[permalink] [raw]
Subject: Re: [PATCH v13 12/14] perf, tools: Add support for event list topics

On Wed, Jun 03, 2015 at 05:57:33AM -0700, Andi Kleen wrote:
> > please split at least the jevents Topic parsing from the rest
> > idelay also the alias update and the display change
>
> What's the point of all these splits? It's already one logical unit,
> not too large, and is bisectable.

splitting the patch in logical pieces helps review and distro
backporting

You changed the parsing tool and perf alias code that uses
the new output. IMO it's separate enough to be placed into
separate patches.

I believe the review would have been easier for me if those changes
were separate, also easing my job when backporting this change later
into the distro

jirka

2015-06-16 09:29:27

by Robert Richter

[permalink] [raw]
Subject: Re: [PATCH v13 04/14] perf, tools: Allow events with dot

On 03.06.15 12:32:04, Jiri Olsa wrote:
> On Tue, Jun 02, 2015 at 10:12:04AM -0700, Sukadev Bhattiprolu wrote:
> > From: Andi Kleen <[email protected]>
> >
> > The Intel events use a dot to separate event name and unit mask.
> > Allow dot in names in the scanner, and remove special handling
> > of dot as EOF. Also remove the hack in jevents to replace dot
> > with underscore. This way dotted events can be specified
> > directly by the user.
> >
> > I'm not fully sure this change to the scanner is correct
> > (what was the dot special case good for?), but I haven't
> > found anything that breaks with it so far at least.
>
> can't see anything either
>
> Robert,
> does it ring a bell? seems like you introduced it ;-)

It is not a dot, it is a regex to handle any other char or <eof> to
return from <event> state to <INITIAL> and rescan the text again.

The change below does not only add the dot for config but also for
names of groups, pmus and tracepoints (might not be complete, see
PE_NAME).

To be sure this is only valid in config syntax, add

name_config [a-zA-Z_*?][a-zA-Z0-9_*?.]*

and change name to name_config in <config>.

This should be cleaned up with name_minus by merging both.

I also think we can remove PE_NAME from event_term: at all. We need to
return PE_TERM then instead from <config>.

If you need help with the changes, I could then prepare something and
send it to you, let me know.

-Robert

>
> thanks,
> jirka
>
>
> >
> > V2: Add the dot to name too, to handle events outside cpu//
> > Acked-by: Namhyung Kim <[email protected]>
> > Signed-off-by: Andi Kleen <[email protected]>
> > Signed-off-by: Sukadev Bhattiprolu <[email protected]>
> > ---
> > tools/perf/util/parse-events.l | 5 ++---
> > 1 file changed, 2 insertions(+), 3 deletions(-)
> >
> > diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
> > index 09e738f..13cef3c 100644
> > --- a/tools/perf/util/parse-events.l
> > +++ b/tools/perf/util/parse-events.l
> > @@ -119,8 +119,8 @@ event [^,{}/]+
> > num_dec [0-9]+
> > num_hex 0x[a-fA-F0-9]+
> > num_raw_hex [a-fA-F0-9]+
> > -name [a-zA-Z_*?][a-zA-Z0-9_*?]*
> > -name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]*
> > +name [a-zA-Z_*?][a-zA-Z0-9_*?.]*
> > +name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]*
> > /* If you add a modifier you need to update check_modifier() */
> > modifier_event [ukhpGHSDI]+
> > modifier_bp [rwx]{1,3}
> > @@ -165,7 +165,6 @@ modifier_bp [rwx]{1,3}
> > return PE_EVENT_NAME;
> > }
> >
> > -. |
> > <<EOF>> {
> > BEGIN(INITIAL);
> > REWIND(0);
> > --
> > 1.7.9.5
> >