Commit c2b8d8c5 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-2' of...

Merge tag 'perf-core-for-mingo-2' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

New features:

  - Add API to set values of map entries in a BPF object, be it
    individual map slots or ranges (Wang Nan)

  - Introduce support for the 'bpf-output' event (Wang Nan)

  - Add glue to read perf events in a BPF program (Wang Nan)

User visible changes:

  - Don't stop PMU parsing on alias parse error, allowing the
    addition of new sysfs PMU files without breaking old tools (Andi Kleen)

  - Implement '%' operation in libtraceevent (Daniel Bristot de Oliveira)

  - Allow specifying events via -e in 'perf mem record', also listing what events
    can be specified via 'perf mem record -e list' (Jiri Olsa)

  - Improve support to 'data_src', 'weight' and 'addr' fields in
    'perf script' (Jiri Olsa)

Infrastructure changes:

  - Export cacheline routines (Jiri Olsa)

  - Remove strbuf_{remove,splice}(), dead code (Arnaldo Carvalho de Melo)

Fixes:

  - Sort key fixes: Alignment for srcline, file, trace; fix
    segfault for dynamic, trace events related sort keys (Namyung Kim)

Build fixes:

  - Remove duplicate typedef config_term_func_t definition,
    fixing the build on older systems (Arnaldo Carvalho de Melo)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 91e48b7d bea24006
......@@ -1951,6 +1951,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
strcmp(token, "*") == 0 ||
strcmp(token, "^") == 0 ||
strcmp(token, "/") == 0 ||
strcmp(token, "%") == 0 ||
strcmp(token, "<") == 0 ||
strcmp(token, ">") == 0 ||
strcmp(token, "<=") == 0 ||
......@@ -3689,6 +3690,9 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
case '/':
val = left / right;
break;
case '%':
val = left % right;
break;
case '*':
val = left * right;
break;
......
......@@ -86,8 +86,7 @@ static int check_emacsclient_version(void)
return -1;
}
strbuf_remove(&buffer, 0, strlen("emacsclient"));
version = atoi(buffer.buf);
version = atoi(buffer.buf + strlen("emacsclient"));
if (version < 22) {
fprintf(stderr,
......
......@@ -6,6 +6,8 @@
#include "util/tool.h"
#include "util/session.h"
#include "util/data.h"
#include "util/mem-events.h"
#include "util/debug.h"
#define MEM_OPERATION_LOAD 0x1
#define MEM_OPERATION_STORE 0x2
......@@ -21,11 +23,55 @@ struct perf_mem {
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
static int parse_record_events(const struct option *opt,
const char *str, int unset __maybe_unused)
{
struct perf_mem *mem = *(struct perf_mem **)opt->value;
int j;
if (strcmp(str, "list")) {
if (!perf_mem_events__parse(str)) {
mem->operation = 0;
return 0;
}
exit(-1);
}
for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
struct perf_mem_event *e = &perf_mem_events[j];
fprintf(stderr, "%-20s%s",
e->tag, verbose ? "" : "\n");
if (verbose)
fprintf(stderr, " [%s]\n", e->name);
}
exit(0);
}
static const char * const __usage[] = {
"perf mem record [<options>] [<command>]",
"perf mem record [<options>] -- <command> [<options>]",
NULL
};
static const char * const *record_mem_usage = __usage;
static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
{
int rec_argc, i = 0, j;
const char **rec_argv;
int ret;
struct option options[] = {
OPT_CALLBACK('e', "event", &mem, "event",
"event selector. use 'perf mem record -e list' to list available events",
parse_record_events),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
OPT_END()
};
argc = parse_options(argc, argv, options, record_mem_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
rec_argc = argc + 7; /* max number of arguments */
rec_argv = calloc(rec_argc + 1, sizeof(char *));
......@@ -35,23 +81,34 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
rec_argv[i++] = "record";
if (mem->operation & MEM_OPERATION_LOAD)
perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true;
if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record)
rec_argv[i++] = "-W";
rec_argv[i++] = "-d";
if (mem->operation & MEM_OPERATION_LOAD) {
rec_argv[i++] = "-e";
rec_argv[i++] = "cpu/mem-loads/pp";
}
for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
if (!perf_mem_events[j].record)
continue;
if (mem->operation & MEM_OPERATION_STORE) {
rec_argv[i++] = "-e";
rec_argv[i++] = "cpu/mem-stores/pp";
}
rec_argv[i++] = perf_mem_events[j].name;
};
for (j = 1; j < argc; j++, i++)
for (j = 0; j < argc; j++, i++)
rec_argv[i] = argv[j];
if (verbose > 0) {
pr_debug("calling: record ");
while (rec_argv[j]) {
pr_debug("%s ", rec_argv[j]);
j++;
}
pr_debug("\n");
}
ret = cmd_record(i, rec_argv, NULL);
free(rec_argv);
return ret;
......@@ -298,7 +355,6 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
NULL
};
argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
mem_usage, PARSE_OPT_STOP_AT_NON_OPTION);
......
......@@ -32,6 +32,7 @@
#include "util/parse-branch-options.h"
#include "util/parse-regs-options.h"
#include "util/llvm-utils.h"
#include "util/bpf-loader.h"
#include <unistd.h>
#include <sched.h>
......@@ -536,6 +537,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
goto out_child;
}
err = bpf__apply_obj_config();
if (err) {
char errbuf[BUFSIZ];
bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf));
pr_err("ERROR: Apply config to BPF failed: %s\n",
errbuf);
goto out_child;
}
/*
* Normally perf_session__new would do this, but it doesn't have the
* evlist.
......
......@@ -58,6 +58,8 @@ enum perf_output_field {
PERF_OUTPUT_IREGS = 1U << 14,
PERF_OUTPUT_BRSTACK = 1U << 15,
PERF_OUTPUT_BRSTACKSYM = 1U << 16,
PERF_OUTPUT_DATA_SRC = 1U << 17,
PERF_OUTPUT_WEIGHT = 1U << 18,
};
struct output_option {
......@@ -81,6 +83,8 @@ struct output_option {
{.str = "iregs", .field = PERF_OUTPUT_IREGS},
{.str = "brstack", .field = PERF_OUTPUT_BRSTACK},
{.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM},
{.str = "data_src", .field = PERF_OUTPUT_DATA_SRC},
{.str = "weight", .field = PERF_OUTPUT_WEIGHT},
};
/* default set to maintain compatibility with current format */
......@@ -131,7 +135,8 @@ static struct {
PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
PERF_OUTPUT_PERIOD,
PERF_OUTPUT_PERIOD | PERF_OUTPUT_ADDR |
PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT,
.invalid_fields = PERF_OUTPUT_TRACE,
},
......@@ -242,6 +247,16 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
PERF_OUTPUT_ADDR, allow_user_set))
return -EINVAL;
if (PRINT_FIELD(DATA_SRC) &&
perf_evsel__check_stype(evsel, PERF_SAMPLE_DATA_SRC, "DATA_SRC",
PERF_OUTPUT_DATA_SRC))
return -EINVAL;
if (PRINT_FIELD(WEIGHT) &&
perf_evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT, "WEIGHT",
PERF_OUTPUT_WEIGHT))
return -EINVAL;
if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {
pr_err("Display of symbols requested but neither sample IP nor "
"sample address\nis selected. Hence, no addresses to convert "
......@@ -673,6 +688,12 @@ static void process_event(struct perf_script *script, union perf_event *event,
if (PRINT_FIELD(ADDR))
print_sample_addr(event, sample, thread, attr);
if (PRINT_FIELD(DATA_SRC))
printf("%16" PRIx64, sample->data_src);
if (PRINT_FIELD(WEIGHT))
printf("%16" PRIu64, sample->weight);
if (PRINT_FIELD(IP)) {
if (!symbol_conf.use_callchain)
printf(" ");
......
......@@ -112,7 +112,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
parse_evlist.error = &parse_error;
INIT_LIST_HEAD(&parse_evlist.list);
err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj, NULL);
if (err || list_empty(&parse_evlist.list)) {
pr_debug("Failed to add events selected by BPF\n");
return TEST_FAIL;
......
......@@ -645,6 +645,9 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
if (perf_hpp__is_sort_entry(fmt))
return perf_hpp__reset_sort_width(fmt, hists);
if (perf_hpp__is_dynamic_entry(fmt))
return;
BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX);
switch (fmt->idx) {
......
......@@ -82,6 +82,7 @@ libperf-y += parse-branch-options.o
libperf-y += parse-regs-options.o
libperf-y += term.o
libperf-y += help-unknown-cmd.o
libperf-y += mem-events.o
libperf-$(CONFIG_LIBBPF) += bpf-loader.o
libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
......
......@@ -7,6 +7,7 @@
#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <linux/err.h>
#include <linux/string.h>
#include "perf.h"
......@@ -16,6 +17,7 @@
#include "llvm-utils.h"
#include "probe-event.h"
#include "probe-finder.h" // for MAX_PROBES
#include "parse-events.h"
#include "llvm-utils.h"
#define DEFINE_PRINT_FN(name, level) \
......@@ -739,6 +741,682 @@ int bpf__foreach_tev(struct bpf_object *obj,
return 0;
}
enum bpf_map_op_type {
BPF_MAP_OP_SET_VALUE,
BPF_MAP_OP_SET_EVSEL,
};
enum bpf_map_key_type {
BPF_MAP_KEY_ALL,
BPF_MAP_KEY_RANGES,
};
struct bpf_map_op {
struct list_head list;
enum bpf_map_op_type op_type;
enum bpf_map_key_type key_type;
union {
struct parse_events_array array;
} k;
union {
u64 value;
struct perf_evsel *evsel;
} v;
};
struct bpf_map_priv {
struct list_head ops_list;
};
static void
bpf_map_op__delete(struct bpf_map_op *op)
{
if (!list_empty(&op->list))
list_del(&op->list);
if (op->key_type == BPF_MAP_KEY_RANGES)
parse_events__clear_array(&op->k.array);
free(op);
}
static void
bpf_map_priv__purge(struct bpf_map_priv *priv)
{
struct bpf_map_op *pos, *n;
list_for_each_entry_safe(pos, n, &priv->ops_list, list) {
list_del_init(&pos->list);
bpf_map_op__delete(pos);
}
}
static void
bpf_map_priv__clear(struct bpf_map *map __maybe_unused,
void *_priv)
{
struct bpf_map_priv *priv = _priv;
bpf_map_priv__purge(priv);
free(priv);
}
static int
bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term)
{
op->key_type = BPF_MAP_KEY_ALL;
if (!term)
return 0;
if (term->array.nr_ranges) {
size_t memsz = term->array.nr_ranges *
sizeof(op->k.array.ranges[0]);
op->k.array.ranges = memdup(term->array.ranges, memsz);
if (!op->k.array.ranges) {
pr_debug("No enough memory to alloc indices for map\n");
return -ENOMEM;
}
op->key_type = BPF_MAP_KEY_RANGES;
op->k.array.nr_ranges = term->array.nr_ranges;
}
return 0;
}
static struct bpf_map_op *
bpf_map_op__new(struct parse_events_term *term)
{
struct bpf_map_op *op;
int err;
op = zalloc(sizeof(*op));
if (!op) {
pr_debug("Failed to alloc bpf_map_op\n");
return ERR_PTR(-ENOMEM);
}
INIT_LIST_HEAD(&op->list);
err = bpf_map_op_setkey(op, term);
if (err) {
free(op);
return ERR_PTR(err);
}
return op;
}
static int
bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)
{
struct bpf_map_priv *priv;
const char *map_name;
int err;
map_name = bpf_map__get_name(map);
err = bpf_map__get_private(map, (void **)&priv);
if (err) {
pr_debug("Failed to get private from map %s\n", map_name);
return err;
}
if (!priv) {
priv = zalloc(sizeof(*priv));
if (!priv) {
pr_debug("No enough memory to alloc map private\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&priv->ops_list);
if (bpf_map__set_private(map, priv, bpf_map_priv__clear)) {
free(priv);
return -BPF_LOADER_ERRNO__INTERNAL;
}
}
list_add_tail(&op->list, &priv->ops_list);
return 0;
}
static struct bpf_map_op *
bpf_map__add_newop(struct bpf_map *map, struct parse_events_term *term)
{
struct bpf_map_op *op;
int err;
op = bpf_map_op__new(term);
if (IS_ERR(op))
return op;
err = bpf_map__add_op(map, op);
if (err) {
bpf_map_op__delete(op);
return ERR_PTR(err);
}
return op;
}
static int
__bpf_map__config_value(struct bpf_map *map,
struct parse_events_term *term)
{
struct bpf_map_def def;
struct bpf_map_op *op;
const char *map_name;
int err;
map_name = bpf_map__get_name(map);
err = bpf_map__get_def(map, &def);
if (err) {
pr_debug("Unable to get map definition from '%s'\n",
map_name);
return -BPF_LOADER_ERRNO__INTERNAL;
}
if (def.type != BPF_MAP_TYPE_ARRAY) {
pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n",
map_name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
}
if (def.key_size < sizeof(unsigned int)) {
pr_debug("Map %s has incorrect key size\n", map_name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE;
}
switch (def.value_size) {
case 1:
case 2:
case 4:
case 8:
break;
default:
pr_debug("Map %s has incorrect value size\n", map_name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
}
op = bpf_map__add_newop(map, term);
if (IS_ERR(op))
return PTR_ERR(op);
op->op_type = BPF_MAP_OP_SET_VALUE;
op->v.value = term->val.num;
return 0;
}
static int
bpf_map__config_value(struct bpf_map *map,
struct parse_events_term *term,
struct perf_evlist *evlist __maybe_unused)
{
if (!term->err_val) {
pr_debug("Config value not set\n");
return -BPF_LOADER_ERRNO__OBJCONF_CONF;
}
if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) {
pr_debug("ERROR: wrong value type for 'value'\n");
return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
}
return __bpf_map__config_value(map, term);
}
static int
__bpf_map__config_event(struct bpf_map *map,
struct parse_events_term *term,
struct perf_evlist *evlist)
{
struct perf_evsel *evsel;
struct bpf_map_def def;
struct bpf_map_op *op;
const char *map_name;
int err;
map_name = bpf_map__get_name(map);
evsel = perf_evlist__find_evsel_by_str(evlist, term->val.str);
if (!evsel) {
pr_debug("Event (for '%s') '%s' doesn't exist\n",
map_name, term->val.str);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT;
}
err = bpf_map__get_def(map, &def);
if (err) {
pr_debug("Unable to get map definition from '%s'\n",
map_name);
return err;
}
/*
* No need to check key_size and value_size:
* kernel has already checked them.
*/
if (def.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
map_name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
}
op = bpf_map__add_newop(map, term);
if (IS_ERR(op))
return PTR_ERR(op);
op->op_type = BPF_MAP_OP_SET_EVSEL;
op->v.evsel = evsel;
return 0;
}
static int
bpf_map__config_event(struct bpf_map *map,
struct parse_events_term *term,
struct perf_evlist *evlist)
{
if (!term->err_val) {
pr_debug("Config value not set\n");
return -BPF_LOADER_ERRNO__OBJCONF_CONF;
}
if (term->type_val != PARSE_EVENTS__TERM_TYPE_STR) {
pr_debug("ERROR: wrong value type for 'event'\n");
return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
}
return __bpf_map__config_event(map, term, evlist);
}
struct bpf_obj_config__map_func {
const char *config_opt;
int (*config_func)(struct bpf_map *, struct parse_events_term *,
struct perf_evlist *);
};
struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = {
{"value", bpf_map__config_value},
{"event", bpf_map__config_event},
};
static int
config_map_indices_range_check(struct parse_events_term *term,
struct bpf_map *map,
const char *map_name)
{
struct parse_events_array *array = &term->array;
struct bpf_map_def def;
unsigned int i;
int err;
if (!array->nr_ranges)
return 0;
if (!array->ranges) {
pr_debug("ERROR: map %s: array->nr_ranges is %d but range array is NULL\n",
map_name, (int)array->nr_ranges);
return -BPF_LOADER_ERRNO__INTERNAL;
}
err = bpf_map__get_def(map, &def);
if (err) {
pr_debug("ERROR: Unable to get map definition from '%s'\n",
map_name);
return -BPF_LOADER_ERRNO__INTERNAL;
}
for (i = 0; i < array->nr_ranges; i++) {
unsigned int start = array->ranges[i].start;
size_t length = array->ranges[i].length;
unsigned int idx = start + length - 1;
if (idx >= def.max_entries) {
pr_debug("ERROR: index %d too large\n", idx);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG;
}
}
return 0;
}
static int
bpf__obj_config_map(struct bpf_object *obj,
struct parse_events_term *term,
struct perf_evlist *evlist,
int *key_scan_pos)
{
/* key is "map:<mapname>.<config opt>" */
char *map_name = strdup(term->config + sizeof("map:") - 1);
struct bpf_map *map;
int err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
char *map_opt;
size_t i;
if (!map_name)
return -ENOMEM;
map_opt = strchr(map_name, '.');
if (!map_opt) {
pr_debug("ERROR: Invalid map config: %s\n", map_name);
goto out;
}
*map_opt++ = '\0';
if (*map_opt == '\0') {
pr_debug("ERROR: Invalid map option: %s\n", term->config);
goto out;
}
map = bpf_object__get_map_by_name(obj, map_name);
if (!map) {
pr_debug("ERROR: Map %s doesn't exist\n", map_name);
err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST;
goto out;
}
*key_scan_pos += strlen(map_opt);
err = config_map_indices_range_check(term, map, map_name);
if (err)
goto out;
*key_scan_pos -= strlen(map_opt);
for (i = 0; i < ARRAY_SIZE(bpf_obj_config__map_funcs); i++) {
struct bpf_obj_config__map_func *func =
&bpf_obj_config__map_funcs[i];
if (strcmp(map_opt, func->config_opt) == 0) {
err = func->config_func(map, term, evlist);
goto out;
}
}
pr_debug("ERROR: Invalid map config option '%s'\n", map_opt);
err = -BPF_LOADER_ERRNO__OBJCONF_MAP_OPT;
out:
free(map_name);
if (!err)
key_scan_pos += strlen(map_opt);
return err;
}
int bpf__config_obj(struct bpf_object *obj,
struct parse_events_term *term,
struct perf_evlist *evlist,
int *error_pos)
{
int key_scan_pos = 0;
int err;
if (!obj || !term || !term->config)
return -EINVAL;
if (!prefixcmp(term->config, "map:")) {
key_scan_pos = sizeof("map:") - 1;
err = bpf__obj_config_map(obj, term, evlist, &key_scan_pos);
goto out;
}
err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
out:
if (error_pos)
*error_pos = key_scan_pos;
return err;
}
typedef int (*map_config_func_t)(const char *name, int map_fd,
struct bpf_map_def *pdef,
struct bpf_map_op *op,
void *pkey, void *arg);
static int
foreach_key_array_all(map_config_func_t func,
void *arg, const char *name,
int map_fd, struct bpf_map_def *pdef,
struct bpf_map_op *op)
{
unsigned int i;
int err;
for (i = 0; i < pdef->max_entries; i++) {
err = func(name, map_fd, pdef, op, &i, arg);
if (err) {
pr_debug("ERROR: failed to insert value to %s[%u]\n",
name, i);
return err;
}
}
return 0;
}
static int
foreach_key_array_ranges(map_config_func_t func, void *arg,
const char *name, int map_fd,
struct bpf_map_def *pdef,
struct bpf_map_op *op)
{
unsigned int i, j;
int err;
for (i = 0; i < op->k.array.nr_ranges; i++) {
unsigned int start = op->k.array.ranges[i].start;
size_t length = op->k.array.ranges[i].length;
for (j = 0; j < length; j++) {
unsigned int idx = start + j;
err = func(name, map_fd, pdef, op, &idx, arg);
if (err) {
pr_debug("ERROR: failed to insert value to %s[%u]\n",
name, idx);
return err;
}
}
}
return 0;
}
static int
bpf_map_config_foreach_key(struct bpf_map *map,
map_config_func_t func,
void *arg)
{
int err, map_fd;
const char *name;
struct bpf_map_op *op;
struct bpf_map_def def;
struct bpf_map_priv *priv;
name = bpf_map__get_name(map);
err = bpf_map__get_private(map, (void **)&priv);
if (err) {
pr_debug("ERROR: failed to get private from map %s\n", name);
return -BPF_LOADER_ERRNO__INTERNAL;
}
if (!priv || list_empty(&priv->ops_list)) {
pr_debug("INFO: nothing to config for map %s\n", name);
return 0;
}
err = bpf_map__get_def(map, &def);
if (err) {
pr_debug("ERROR: failed to get definition from map %s\n", name);
return -BPF_LOADER_ERRNO__INTERNAL;
}
map_fd = bpf_map__get_fd(map);
if (map_fd < 0) {
pr_debug("ERROR: failed to get fd from map %s\n", name);
return map_fd;
}
list_for_each_entry(op, &priv->ops_list, list) {
switch (def.type) {
case BPF_MAP_TYPE_ARRAY:
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
switch (op->key_type) {
case BPF_MAP_KEY_ALL:
err = foreach_key_array_all(func, arg, name,
map_fd, &def, op);
break;
case BPF_MAP_KEY_RANGES:
err = foreach_key_array_ranges(func, arg, name,
map_fd, &def,
op);
break;
default:
pr_debug("ERROR: keytype for map '%s' invalid\n",
name);
return -BPF_LOADER_ERRNO__INTERNAL;
}
if (err)
return err;
break;
default:
pr_debug("ERROR: type of '%s' incorrect\n", name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
}
}
return 0;
}
static int
apply_config_value_for_key(int map_fd, void *pkey,
size_t val_size, u64 val)
{
int err = 0;
switch (val_size) {
case 1: {
u8 _val = (u8)(val);
err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
break;
}
case 2: {
u16 _val = (u16)(val);
err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
break;
}
case 4: {
u32 _val = (u32)(val);
err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
break;
}
case 8: {
err = bpf_map_update_elem(map_fd, pkey, &val, BPF_ANY);
break;
}
default:
pr_debug("ERROR: invalid value size\n");
return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
}
if (err && errno)
err = -errno;
return err;
}
static int
apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,
struct perf_evsel *evsel)
{
struct xyarray *xy = evsel->fd;
struct perf_event_attr *attr;
unsigned int key, events;
bool check_pass = false;
int *evt_fd;
int err;
if (!xy) {
pr_debug("ERROR: evsel not ready for map %s\n", name);
return -BPF_LOADER_ERRNO__INTERNAL;
}
if (xy->row_size / xy->entry_size != 1) {
pr_debug("ERROR: Dimension of target event is incorrect for map %s\n",
name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM;
}
attr = &evsel->attr;
if (attr->inherit) {
pr_debug("ERROR: Can't put inherit event into map %s\n", name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH;
}
if (perf_evsel__is_bpf_output(evsel))
check_pass = true;
if (attr->type == PERF_TYPE_RAW)
check_pass = true;
if (attr->type == PERF_TYPE_HARDWARE)
check_pass = true;
if (!check_pass) {
pr_debug("ERROR: Event type is wrong for map %s\n", name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE;
}
events = xy->entries / (xy->row_size / xy->entry_size);
key = *((unsigned int *)pkey);
if (key >= events) {
pr_debug("ERROR: there is no event %d for map %s\n",
key, name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE;
}
evt_fd = xyarray__entry(xy, key, 0);
err = bpf_map_update_elem(map_fd, pkey, evt_fd, BPF_ANY);
if (err && errno)
err = -errno;
return err;
}
static int
apply_obj_config_map_for_key(const char *name, int map_fd,
struct bpf_map_def *pdef __maybe_unused,
struct bpf_map_op *op,
void *pkey, void *arg __maybe_unused)
{
int err;
switch (op->op_type) {
case BPF_MAP_OP_SET_VALUE:
err = apply_config_value_for_key(map_fd, pkey,
pdef->value_size,
op->v.value);
break;
case BPF_MAP_OP_SET_EVSEL:
err = apply_config_evsel_for_key(name, map_fd, pkey,
op->v.evsel);
break;
default:
pr_debug("ERROR: unknown value type for '%s'\n", name);
err = -BPF_LOADER_ERRNO__INTERNAL;
}
return err;
}
static int
apply_obj_config_map(struct bpf_map *map)
{
return bpf_map_config_foreach_key(map,
apply_obj_config_map_for_key,
NULL);
}
static int
apply_obj_config_object(struct bpf_object *obj)
{
struct bpf_map *map;
int err;
bpf_map__for_each(map, obj) {
err = apply_obj_config_map(map);
if (err)
return err;
}
return 0;
}
int bpf__apply_obj_config(void)
{
struct bpf_object *obj, *tmp;
int err;
bpf_object__for_each_safe(obj, tmp) {
err = apply_obj_config_object(obj);
if (err)
return err;
}
return 0;
}
#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START)
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
......@@ -753,6 +1431,20 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
[ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
[ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
[ERRCODE_OFFSET(OBJCONF_OPT)] = "Invalid object config option",
[ERRCODE_OFFSET(OBJCONF_CONF)] = "Config value not set (missing '=')",
[ERRCODE_OFFSET(OBJCONF_MAP_OPT)] = "Invalid object map config option",
[ERRCODE_OFFSET(OBJCONF_MAP_NOTEXIST)] = "Target map doesn't exist",
[ERRCODE_OFFSET(OBJCONF_MAP_VALUE)] = "Incorrect value type for map",
[ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type",
[ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size",
[ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size",
[ERRCODE_OFFSET(OBJCONF_MAP_NOEVT)] = "Event not found for map setting",
[ERRCODE_OFFSET(OBJCONF_MAP_MAPSIZE)] = "Invalid map size for event setting",
[ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large",
[ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event",
[ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map",
[ERRCODE_OFFSET(OBJCONF_MAP_IDX2BIG)] = "Index too large",
};
static int
......@@ -872,3 +1564,29 @@ int bpf__strerror_load(struct bpf_object *obj,
bpf__strerror_end(buf, size);
return 0;
}
int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
struct parse_events_term *term __maybe_unused,
struct perf_evlist *evlist __maybe_unused,
int *error_pos __maybe_unused, int err,
char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE,
"Can't use this config term with this map type");
bpf__strerror_end(buf, size);
return 0;
}
int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM,
"Cannot set event to BPF map in multi-thread tracing");
bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH,
"%s (Hint: use -i to turn off inherit)", emsg);
bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE,
"Can only put raw, hardware and BPF output event into a BPF map");
bpf__strerror_end(buf, size);
return 0;
}
......@@ -10,6 +10,7 @@
#include <string.h>
#include <bpf/libbpf.h>
#include "probe-event.h"
#include "evlist.h"
#include "debug.h"
enum bpf_loader_errno {
......@@ -24,10 +25,25 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */
BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */
BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */
BPF_LOADER_ERRNO__OBJCONF_OPT, /* Invalid object config option */
BPF_LOADER_ERRNO__OBJCONF_CONF, /* Config value not set (lost '=')) */
BPF_LOADER_ERRNO__OBJCONF_MAP_OPT, /* Invalid object map config option */
BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST, /* Target map not exist */
BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE, /* Incorrect value type for map */
BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, /* Incorrect map type */
BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE, /* Incorrect map key size */
BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE,/* Incorrect map value size */
BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT, /* Event not found for map setting */
BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE, /* Invalid map size for event setting */
BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, /* Event dimension too large */
BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, /* Doesn't support inherit event */
BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, /* Wrong event type for map */
BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG, /* Index too large */
__BPF_LOADER_ERRNO__END,
};
struct bpf_object;
struct parse_events_term;
#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
......@@ -53,6 +69,16 @@ int bpf__strerror_load(struct bpf_object *obj, int err,
char *buf, size_t size);
int bpf__foreach_tev(struct bpf_object *obj,
bpf_prog_iter_callback_t func, void *arg);
int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term,
struct perf_evlist *evlist, int *error_pos);
int bpf__strerror_config_obj(struct bpf_object *obj,
struct parse_events_term *term,
struct perf_evlist *evlist,
int *error_pos, int err, char *buf,
size_t size);
int bpf__apply_obj_config(void);
int bpf__strerror_apply_obj_config(int err, char *buf, size_t size);
#else
static inline struct bpf_object *
bpf__prepare_load(const char *filename __maybe_unused,
......@@ -83,6 +109,21 @@ bpf__foreach_tev(struct bpf_object *obj __maybe_unused,
return 0;
}
static inline int
bpf__config_obj(struct bpf_object *obj __maybe_unused,
struct parse_events_term *term __maybe_unused,
struct perf_evlist *evlist __maybe_unused,
int *error_pos __maybe_unused)
{
return 0;
}
static inline int
bpf__apply_obj_config(void)
{
return 0;
}
static inline int
__bpf_strerror(char *buf, size_t size)
{
......@@ -118,5 +159,23 @@ static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
{
return __bpf_strerror(buf, size);
}
static inline int
bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
struct parse_events_term *term __maybe_unused,
struct perf_evlist *evlist __maybe_unused,
int *error_pos __maybe_unused,
int err __maybe_unused,
char *buf, size_t size)
{
return __bpf_strerror(buf, size);
}
static inline int
bpf__strerror_apply_obj_config(int err __maybe_unused,
char *buf, size_t size)
{
return __bpf_strerror(buf, size);
}
#endif
#endif
......@@ -1741,3 +1741,19 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
tracking_evsel->tracking = true;
}
struct perf_evsel *
perf_evlist__find_evsel_by_str(struct perf_evlist *evlist,
const char *str)
{
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel) {
if (!evsel->name)
continue;
if (strcmp(str, evsel->name) == 0)
return evsel;
}
return NULL;
}
......@@ -294,4 +294,7 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
struct perf_evsel *tracking_evsel);
void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr);
struct perf_evsel *
perf_evlist__find_evsel_by_str(struct perf_evlist *evlist, const char *str);
#endif /* __PERF_EVLIST_H */
......@@ -225,6 +225,11 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
if (evsel != NULL)
perf_evsel__init(evsel, attr, idx);
if (perf_evsel__is_bpf_output(evsel)) {
evsel->attr.sample_type |= PERF_SAMPLE_RAW;
evsel->attr.sample_period = 1;
}
return evsel;
}
......
......@@ -364,6 +364,14 @@ static inline bool perf_evsel__is_function_event(struct perf_evsel *evsel)
#undef FUNCTION_EVENT
}
static inline bool perf_evsel__is_bpf_output(struct perf_evsel *evsel)
{
struct perf_event_attr *attr = &evsel->attr;
return (attr->config == PERF_COUNT_SW_BPF_OUTPUT) &&
(attr->type == PERF_TYPE_SOFTWARE);
}
struct perf_attr_details {
bool freq;
bool verbose;
......
......@@ -179,6 +179,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
if (h->transaction)
hists__new_col_len(hists, HISTC_TRANSACTION,
hist_entry__transaction_len());
if (h->trace_output)
hists__new_col_len(hists, HISTC_TRACE, strlen(h->trace_output));
}
void hists__output_recalc_col_len(struct hists *hists, int max_rows)
......
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "mem-events.h"
#include "debug.h"
#define E(t, n) { .tag = t, .name = n }
struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
E("ldlat-loads", "cpu/mem-loads,ldlat=30/P"),
E("ldlat-stores", "cpu/mem-stores/P"),
};
#undef E
int perf_mem_events__parse(const char *str)
{
char *tok, *saveptr = NULL;
bool found = false;
char *buf;
int j;
/* We need buffer that we know we can write to. */
buf = malloc(strlen(str) + 1);
if (!buf)
return -ENOMEM;
strcpy(buf, str);
tok = strtok_r((char *)buf, ",", &saveptr);
while (tok) {
for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
struct perf_mem_event *e = &perf_mem_events[j];
if (strstr(e->tag, tok))
e->record = found = true;
}
tok = strtok_r(NULL, ",", &saveptr);
}
free(buf);
if (found)
return 0;
pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
return -1;
}
#ifndef __PERF_MEM_EVENTS_H
#define __PERF_MEM_EVENTS_H
#include <stdbool.h>
struct perf_mem_event {
bool record;
const char *tag;
const char *name;
};
enum {
PERF_MEM_EVENTS__LOAD,
PERF_MEM_EVENTS__STORE,
PERF_MEM_EVENTS__MAX,
};
extern struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX];
int perf_mem_events__parse(const char *str);
#endif /* __PERF_MEM_EVENTS_H */
......@@ -363,7 +363,7 @@ static int config_attr(struct perf_event_attr *attr,
int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2,
struct parse_events_error *error,
struct parse_events_error *err,
struct list_head *head_config)
{
struct perf_event_attr attr;
......@@ -425,7 +425,7 @@ int parse_events_add_cache(struct list_head *list, int *idx,
attr.type = PERF_TYPE_HW_CACHE;
if (head_config) {
if (config_attr(&attr, head_config, error,
if (config_attr(&attr, head_config, err,
config_term_common))
return -EINVAL;
......@@ -581,6 +581,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
struct __add_bpf_event_param {
struct parse_events_evlist *data;
struct list_head *list;
struct list_head *head_config;
};
static int add_bpf_event(struct probe_trace_event *tev, int fd,
......@@ -597,7 +598,8 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd,
tev->group, tev->event, fd);
err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group,
tev->event, evlist->error, NULL);
tev->event, evlist->error,
param->head_config);
if (err) {
struct perf_evsel *evsel, *tmp;
......@@ -622,11 +624,12 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd,
int parse_events_load_bpf_obj(struct parse_events_evlist *data,
struct list_head *list,
struct bpf_object *obj)
struct bpf_object *obj,
struct list_head *head_config)
{
int err;
char errbuf[BUFSIZ];
struct __add_bpf_event_param param = {data, list};
struct __add_bpf_event_param param = {data, list, head_config};
static bool registered_unprobe_atexit = false;
if (IS_ERR(obj) || !obj) {
......@@ -672,17 +675,99 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data,
return err;
}
static int
parse_events_config_bpf(struct parse_events_evlist *data,
struct bpf_object *obj,
struct list_head *head_config)
{
struct parse_events_term *term;
int error_pos;
if (!head_config || list_empty(head_config))
return 0;
list_for_each_entry(term, head_config, list) {
char errbuf[BUFSIZ];
int err;
if (term->type_term != PARSE_EVENTS__TERM_TYPE_USER) {
snprintf(errbuf, sizeof(errbuf),
"Invalid config term for BPF object");
errbuf[BUFSIZ - 1] = '\0';
data->error->idx = term->err_term;
data->error->str = strdup(errbuf);
return -EINVAL;
}
err = bpf__config_obj(obj, term, data->evlist, &error_pos);
if (err) {
bpf__strerror_config_obj(obj, term, data->evlist,
&error_pos, err, errbuf,
sizeof(errbuf));
data->error->help = strdup(
"Hint:\tValid config terms:\n"
" \tmap:[<arraymap>].value<indices>=[value]\n"
" \tmap:[<eventmap>].event<indices>=[event]\n"
"\n"
" \twhere <indices> is something like [0,3...5] or [all]\n"
" \t(add -v to see detail)");
data->error->str = strdup(errbuf);
if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE)
data->error->idx = term->err_val;
else
data->error->idx = term->err_term + error_pos;
return err;
}
}
return 0;
}
/*
* Split config terms:
* perf record -e bpf.c/call-graph=fp,map:array.value[0]=1/ ...
* 'call-graph=fp' is 'evt config', should be applied to each
* events in bpf.c.
* 'map:array.value[0]=1' is 'obj config', should be processed
* with parse_events_config_bpf.
*
* Move object config terms from the first list to obj_head_config.
*/
static void
split_bpf_config_terms(struct list_head *evt_head_config,
struct list_head *obj_head_config)
{
struct parse_events_term *term, *temp;
/*
* Currectly, all possible user config term
* belong to bpf object. parse_events__is_hardcoded_term()
* happends to be a good flag.
*
* See parse_events_config_bpf() and
* config_term_tracepoint().
*/
list_for_each_entry_safe(term, temp, evt_head_config, list)
if (!parse_events__is_hardcoded_term(term))
list_move_tail(&term->list, obj_head_config);
}
int parse_events_load_bpf(struct parse_events_evlist *data,
struct list_head *list,
char *bpf_file_name,
bool source)
bool source,
struct list_head *head_config)
{
int err;
struct bpf_object *obj;
LIST_HEAD(obj_head_config);
if (head_config)
split_bpf_config_terms(head_config, &obj_head_config);
obj = bpf__prepare_load(bpf_file_name, source);
if (IS_ERR(obj)) {
char errbuf[BUFSIZ];
int err;
err = PTR_ERR(obj);
......@@ -700,7 +785,18 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
return err;
}
return parse_events_load_bpf_obj(data, list, obj);
err = parse_events_load_bpf_obj(data, list, obj, head_config);
if (err)
return err;
err = parse_events_config_bpf(data, obj, &obj_head_config);
/*
* Caller doesn't know anything about obj_head_config,
* so combine them together again before returnning.
*/
if (head_config)
list_splice_tail(&obj_head_config, head_config);
return err;
}
static int
......@@ -841,10 +937,6 @@ void parse_events__shrink_config_terms(void)
config_term_shrinked = true;
}
typedef int config_term_func_t(struct perf_event_attr *attr,
struct parse_events_term *term,
struct parse_events_error *err);
static int config_term_common(struct perf_event_attr *attr,
struct parse_events_term *term,
struct parse_events_error *err)
......@@ -1488,6 +1580,7 @@ int parse_events(struct perf_evlist *evlist, const char *str,
.list = LIST_HEAD_INIT(data.list),
.idx = evlist->nr_entries,
.error = err,
.evlist = evlist,
};
int ret;
......@@ -2163,6 +2256,8 @@ void parse_events_terms__purge(struct list_head *terms)
struct parse_events_term *term, *h;
list_for_each_entry_safe(term, h, terms, list) {
if (term->array.nr_ranges)
free(term->array.ranges);
list_del_init(&term->list);
free(term);
}
......@@ -2176,6 +2271,11 @@ void parse_events_terms__delete(struct list_head *terms)
free(terms);
}
void parse_events__clear_array(struct parse_events_array *a)
{
free(a->ranges);
}
void parse_events_evlist_error(struct parse_events_evlist *data,
int idx, const char *str)
{
......
......@@ -72,8 +72,17 @@ enum {
__PARSE_EVENTS__TERM_TYPE_NR,
};
struct parse_events_array {
size_t nr_ranges;
struct {
unsigned int start;
size_t length;
} *ranges;
};
struct parse_events_term {
char *config;
struct parse_events_array array;
union {
char *str;
u64 num;
......@@ -99,6 +108,7 @@ struct parse_events_evlist {
int idx;
int nr_groups;
struct parse_events_error *error;
struct perf_evlist *evlist;
};
struct parse_events_terms {
......@@ -119,6 +129,7 @@ int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term);
void parse_events_terms__delete(struct list_head *terms);
void parse_events_terms__purge(struct list_head *terms);
void parse_events__clear_array(struct parse_events_array *a);
int parse_events__modifier_event(struct list_head *list, char *str, bool add);
int parse_events__modifier_group(struct list_head *list, char *event_mod);
int parse_events_name(struct list_head *list, char *name);
......@@ -129,12 +140,14 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
int parse_events_load_bpf(struct parse_events_evlist *data,
struct list_head *list,
char *bpf_file_name,
bool source);
bool source,
struct list_head *head_config);
/* Provide this function for perf test */
struct bpf_object;
int parse_events_load_bpf_obj(struct parse_events_evlist *data,
struct list_head *list,
struct bpf_object *obj);
struct bpf_object *obj,
struct list_head *head_config);
int parse_events_add_numeric(struct parse_events_evlist *data,
struct list_head *list,
u32 type, u64 config,
......
......@@ -9,8 +9,8 @@
%{
#include <errno.h>
#include "../perf.h"
#include "parse-events-bison.h"
#include "parse-events.h"
#include "parse-events-bison.h"
char *parse_events_get_text(yyscan_t yyscanner);
YYSTYPE *parse_events_get_lval(yyscan_t yyscanner);
......@@ -111,6 +111,7 @@ do { \
%x mem
%s config
%x event
%x array
group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
......@@ -122,7 +123,7 @@ 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_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]*
/* If you add a modifier you need to update check_modifier() */
modifier_event [ukhpPGHSDI]+
modifier_bp [rwx]{1,3}
......@@ -176,6 +177,14 @@ modifier_bp [rwx]{1,3}
}
<array>{
"]" { BEGIN(config); return ']'; }
{num_dec} { return value(yyscanner, 10); }
{num_hex} { return value(yyscanner, 16); }
, { return ','; }
"\.\.\." { return PE_ARRAY_RANGE; }
}
<config>{
/*
* Please update config_term_names when new static term is added.
......@@ -195,6 +204,8 @@ no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); }
\[all\] { return PE_ARRAY_ALL; }
"[" { BEGIN(array); return '['; }
}
<mem>{
......@@ -237,6 +248,7 @@ cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COU
alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }
/*
* We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately.
......
......@@ -48,6 +48,7 @@ static inc_group_count(struct list_head *list,
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
%token PE_ERROR
%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%token PE_ARRAY_ALL PE_ARRAY_RANGE
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM_HW
%type <num> PE_VALUE_SYM_SW
......@@ -83,6 +84,9 @@ static inc_group_count(struct list_head *list,
%type <head> group_def
%type <head> group
%type <head> groups
%type <array> array
%type <array> array_term
%type <array> array_terms
%union
{
......@@ -94,6 +98,7 @@ static inc_group_count(struct list_head *list,
char *sys;
char *event;
} tracepoint_name;
struct parse_events_array array;
}
%%
......@@ -437,24 +442,26 @@ PE_RAW opt_event_config
}
event_bpf_file:
PE_BPF_OBJECT
PE_BPF_OBJECT opt_event_config
{
struct parse_events_evlist *data = _data;
struct parse_events_error *error = data->error;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_load_bpf(data, list, $1, false));
ABORT_ON(parse_events_load_bpf(data, list, $1, false, $2));
parse_events_terms__delete($2);
$$ = list;
}
|
PE_BPF_SOURCE
PE_BPF_SOURCE opt_event_config
{
struct parse_events_evlist *data = _data;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_load_bpf(data, list, $1, true));
ABORT_ON(parse_events_load_bpf(data, list, $1, true, $2));
parse_events_terms__delete($2);
$$ = list;
}
......@@ -570,6 +577,86 @@ PE_TERM
ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL));
$$ = term;
}
|
PE_NAME array '=' PE_NAME
{
struct parse_events_term *term;
int i;
ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, $4, &@1, &@4));
term->array = $2;
$$ = term;
}
|
PE_NAME array '=' PE_VALUE
{
struct parse_events_term *term;
ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, $4, &@1, &@4));
term->array = $2;
$$ = term;
}
array:
'[' array_terms ']'
{
$$ = $2;
}
|
PE_ARRAY_ALL
{
$$.nr_ranges = 0;
$$.ranges = NULL;
}
array_terms:
array_terms ',' array_term
{
struct parse_events_array new_array;
new_array.nr_ranges = $1.nr_ranges + $3.nr_ranges;
new_array.ranges = malloc(sizeof(new_array.ranges[0]) *
new_array.nr_ranges);
ABORT_ON(!new_array.ranges);
memcpy(&new_array.ranges[0], $1.ranges,
$1.nr_ranges * sizeof(new_array.ranges[0]));
memcpy(&new_array.ranges[$1.nr_ranges], $3.ranges,
$3.nr_ranges * sizeof(new_array.ranges[0]));
free($1.ranges);
free($3.ranges);
$$ = new_array;
}
|
array_term
array_term:
PE_VALUE
{
struct parse_events_array array;
array.nr_ranges = 1;
array.ranges = malloc(sizeof(array.ranges[0]));
ABORT_ON(!array.ranges);
array.ranges[0].start = $1;
array.ranges[0].length = 1;
$$ = array;
}
|
PE_VALUE PE_ARRAY_RANGE PE_VALUE
{
struct parse_events_array array;
ABORT_ON($3 < $1);
array.nr_ranges = 1;
array.ranges = malloc(sizeof(array.ranges[0]));
ABORT_ON(!array.ranges);
array.ranges[0].start = $1;
array.ranges[0].length = $3 - $1 + 1;
$$ = array;
}
sep_dc: ':' |
......
......@@ -284,13 +284,12 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
{
struct dirent *evt_ent;
DIR *event_dir;
int ret = 0;
event_dir = opendir(dir);
if (!event_dir)
return -EINVAL;
while (!ret && (evt_ent = readdir(event_dir))) {
while ((evt_ent = readdir(event_dir))) {
char path[PATH_MAX];
char *name = evt_ent->d_name;
FILE *file;
......@@ -306,17 +305,19 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
snprintf(path, PATH_MAX, "%s/%s", dir, name);
ret = -EINVAL;
file = fopen(path, "r");
if (!file)
break;
if (!file) {
pr_debug("Cannot open %s\n", path);
continue;
}
ret = perf_pmu__new_alias(head, dir, name, file);
if (perf_pmu__new_alias(head, dir, name, file) < 0)
pr_debug("Cannot set up %s\n", name);
fclose(file);
}
closedir(event_dir);
return ret;
return 0;
}
/*
......
......@@ -286,36 +286,35 @@ struct sort_entry sort_sym = {
/* --sort srcline */
static char *hist_entry__get_srcline(struct hist_entry *he)
{
struct map *map = he->ms.map;
if (!map)
return SRCLINE_UNKNOWN;
return get_srcline(map->dso, map__rip_2objdump(map, he->ip),
he->ms.sym, true);
}
static int64_t
sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
{
if (!left->srcline) {
if (!left->ms.map)
left->srcline = SRCLINE_UNKNOWN;
else {
struct map *map = left->ms.map;
left->srcline = get_srcline(map->dso,
map__rip_2objdump(map, left->ip),
left->ms.sym, true);
}
}
if (!right->srcline) {
if (!right->ms.map)
right->srcline = SRCLINE_UNKNOWN;
else {
struct map *map = right->ms.map;
right->srcline = get_srcline(map->dso,
map__rip_2objdump(map, right->ip),
right->ms.sym, true);
}
}
if (!left->srcline)
left->srcline = hist_entry__get_srcline(left);
if (!right->srcline)
right->srcline = hist_entry__get_srcline(right);
return strcmp(right->srcline, left->srcline);
}
static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline);
if (!he->srcline)
he->srcline = hist_entry__get_srcline(he);
return repsep_snprintf(bf, size, "%-.*s", width, he->srcline);
}
struct sort_entry sort_srcline = {
......@@ -329,11 +328,14 @@ struct sort_entry sort_srcline = {
static char no_srcfile[1];
static char *get_srcfile(struct hist_entry *e)
static char *hist_entry__get_srcfile(struct hist_entry *e)
{
char *sf, *p;
struct map *map = e->ms.map;
if (!map)
return no_srcfile;
sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
e->ms.sym, false, true);
if (!strcmp(sf, SRCLINE_UNKNOWN))
......@@ -350,25 +352,21 @@ static char *get_srcfile(struct hist_entry *e)
static int64_t
sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
{
if (!left->srcfile) {
if (!left->ms.map)
left->srcfile = no_srcfile;
else
left->srcfile = get_srcfile(left);
}
if (!right->srcfile) {
if (!right->ms.map)
right->srcfile = no_srcfile;
else
right->srcfile = get_srcfile(right);
}
if (!left->srcfile)
left->srcfile = hist_entry__get_srcfile(left);
if (!right->srcfile)
right->srcfile = hist_entry__get_srcfile(right);
return strcmp(right->srcfile, left->srcfile);
}
static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcfile);
if (!he->srcfile)
he->srcfile = hist_entry__get_srcfile(he);
return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile);
}
struct sort_entry sort_srcfile = {
......@@ -485,9 +483,6 @@ sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
if (right->trace_output == NULL)
right->trace_output = get_trace_output(right);
hists__new_col_len(left->hists, HISTC_TRACE, strlen(left->trace_output));
hists__new_col_len(right->hists, HISTC_TRACE, strlen(right->trace_output));
return strcmp(right->trace_output, left->trace_output);
}
......@@ -498,11 +493,11 @@ static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
evsel = hists_to_evsel(he->hists);
if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
return scnprintf(bf, size, "%-*.*s", width, width, "N/A");
return scnprintf(bf, size, "%-.*s", width, "N/A");
if (he->trace_output == NULL)
he->trace_output = get_trace_output(he);
return repsep_snprintf(bf, size, "%-*.*s", width, width, he->trace_output);
return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output);
}
struct sort_entry sort_trace = {
......@@ -843,7 +838,6 @@ static const char * const tlb_access[] = {
"Walker",
"Fault",
};
#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
......@@ -865,7 +859,7 @@ static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
/* already taken care of */
m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
if (!(m & 0x1))
continue;
if (l) {
......@@ -920,7 +914,6 @@ static const char * const mem_lvl[] = {
"I/O",
"Uncached",
};
#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
......@@ -942,7 +935,7 @@ static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
/* already taken care of */
m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
if (!(m & 0x1))
continue;
if (l) {
......@@ -988,7 +981,6 @@ static const char * const snoop_access[] = {
"Hit",
"HitM",
};
#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
......@@ -1003,7 +995,7 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
if (he->mem_info)
m = he->mem_info->data_src.mem_snoop;
for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
if (!(m & 0x1))
continue;
if (l) {
......@@ -1020,12 +1012,6 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
return repsep_snprintf(bf, size, "%-*s", width, out);
}
static inline u64 cl_address(u64 address)
{
/* return the cacheline of the address */
return (address & ~(cacheline_size - 1));
}
static int64_t
sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
{
......@@ -1835,6 +1821,20 @@ bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)
return fmt->cmp == __sort__hde_cmp;
}
static bool __sort__hde_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
{
struct hpp_dynamic_entry *hde_a;
struct hpp_dynamic_entry *hde_b;
if (!perf_hpp__is_dynamic_entry(a) || !perf_hpp__is_dynamic_entry(b))
return false;
hde_a = container_of(a, struct hpp_dynamic_entry, hpp);
hde_b = container_of(b, struct hpp_dynamic_entry, hpp);
return hde_a->field == hde_b->field;
}
static void hde_free(struct perf_hpp_fmt *fmt)
{
struct hpp_dynamic_entry *hde;
......@@ -1867,6 +1867,7 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field)
hde->hpp.cmp = __sort__hde_cmp;
hde->hpp.collapse = __sort__hde_cmp;
hde->hpp.sort = __sort__hde_cmp;
hde->hpp.equal = __sort__hde_equal;
hde->hpp.free = hde_free;
INIT_LIST_HEAD(&hde->hpp.list);
......
......@@ -162,6 +162,17 @@ static inline float hist_entry__get_percent_limit(struct hist_entry *he)
return period * 100.0 / total_period;
}
static inline u64 cl_address(u64 address)
{
/* return the cacheline of the address */
return (address & ~(cacheline_size - 1));
}
static inline u64 cl_offset(u64 address)
{
/* return the cacheline of the address */
return (address & (cacheline_size - 1));
}
enum sort_mode {
SORT_MODE__NORMAL,
......
......@@ -51,30 +51,6 @@ void strbuf_grow(struct strbuf *sb, size_t extra)
ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
}
static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
const void *data, size_t dlen)
{
if (pos + len < pos)
die("you want to use way too much memory");
if (pos > sb->len)
die("`pos' is too far after the end of the buffer");
if (pos + len > sb->len)
die("`pos + len' is too far after the end of the buffer");
if (dlen >= len)
strbuf_grow(sb, dlen - len);
memmove(sb->buf + pos + dlen,
sb->buf + pos + len,
sb->len - pos - len);
memcpy(sb->buf + pos, data, dlen);
strbuf_setlen(sb, sb->len + dlen - len);
}
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
{
strbuf_splice(sb, pos, len, NULL, 0);
}
void strbuf_add(struct strbuf *sb, const void *data, size_t len)
{
strbuf_grow(sb, len);
......
......@@ -77,8 +77,6 @@ static inline void strbuf_addch(struct strbuf *sb, int c) {
sb->buf[sb->len] = '\0';
}
extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
extern void strbuf_add(struct strbuf *, const void *, size_t);
static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
strbuf_add(sb, s, strlen(s));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment