perf/core improvements and fixes:
User visible: - 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: - Export cacheline routines (Jiri Olsa) - Remove strbuf_{remove,splice}(), dead code (Arnaldo Carvalho de Melo) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWzLl7AAoJENZQFvNTUqpAk1wP/A8Thp/8ayx1/zzw3+wDtPCg duhAVR7bKY5RSbWMXaIwuCSYcMcrq2Kjstj12qHgHac7xqQKll4UDodBFpBb79ir 7V5oJYCXdp5zCQnyYLSnevfvBOh9mkHvHWHnal9IbHJzvP5MtpecZ8rchim/CWM9 e9++EIgfzJFxfPAcXAdokOFquEKRkkob1YKGG3BB4mDbCRI2yR+WfoE5iAFtSQuN fJfSJ12fsHweYojcPEZ3mQh6ZyovYg2LC2LRcTYaI6IBDNs5xY9lMaKjSw9H44Qq loh6yPSWqUCuxmvUdXhNIwkxtce+hMQUIl2Biu38gLUToU/moJA4SyjkNRnfP+fQ 4S9njvPfW0nFApaYPGAVo5MSsz5UB65c5pXP4ptiMhwufUoaQR0DUccxWwINCX8y YUWkkHARPQCe3Mpr1bmuIH0pGCI2iDR6F/+fsEsAsJHBOgCAIut+gtknusj57osv JJPbqVyPD6mwQ6l2fDb9RRPw1Xdhd4ZMk2yRyI7w43xHgD8ZcbwoUE621KslpiBS hQf4zTJFW5csArfxIVpyvcAvbYxHbnJ228TCJGes+gLDfUdP3Vkl4WWC+3kc7KY1 P4wdSucSWZ19iGc+nOr2R8PxqhijGkbUbMo7qnGDfGfjxj6MBDjglqeh/s4qOxbm dIgkYT6xpLh87dnIKiW6 =YXng -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
c2b8d8c55c
@ -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)
|
||||
|
51
tools/perf/util/mem-events.c
Normal file
51
tools/perf/util/mem-events.c
Normal file
@ -0,0 +1,51 @@
|
||||
#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;
|
||||
}
|
22
tools/perf/util/mem-events.h
Normal file
22
tools/perf/util/mem-events.h
Normal file
@ -0,0 +1,22 @@
|
||||
#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 @@ errout:
|
||||
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)
|
||||
@ -1485,9 +1577,10 @@ int parse_events(struct perf_evlist *evlist, const char *str,
|
||||
struct parse_events_error *err)
|
||||
{
|
||||
struct parse_events_evlist data = {
|
||||
.list = LIST_HEAD_INIT(data.list),
|
||||
.idx = evlist->nr_entries,
|
||||
.error = err,
|
||||
.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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user