perf/core improvements and fixes:
. Preparatory patches to use hw events in PMU syntax, from Jiri Olsa . Remaining backport of trace-cmd's libparseevent, from Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJP9bEdAAoJENZQFvNTUqpAvGEP/AtaZBleXnhgmXWmbPNvlx2z 3qBI5vLn3iczITmnK4mTVqGAQ+PZRGX+m7h2rTqyJSMzzkLBHRtjt1Y/Ul5x4pRk 72xaZQh1OCEZCsqKyzYagaRFt3jaqYUElO+nVTaony+Xp8zeJBnG6Jd6C9l5KhKH OHwkQEXv49tnSG5P9JigTEstnPLckGsrdZch2uU8pHLdw1P+LHmKnLeO6+Jt0az7 m7Tr+fQgqIi530xazkg2ZJM/FXVZpwlwAtt8u2U7mUApfpI+eRILrhDyzyUudVWX Hpr9cPVPI/l0LQaWe82asdTMNm7CC9GPwxoaIkjN2DNSQfXKSym+ontrudFGS10O BZFQ595eCA3VUdvMFzqi8x5q4ia/dg+JUmi67GS7tz/IL34NmS6Nsu5FHhVreeRK oZmGZP4wpZ6/Fy1FjaQHBFOpPKD89AVpUN4MsILKh8N9IG74G1Vfia1/EfewKerD DX8UPr3CAbu5WVWq+CUDUVK9bq9PJzW8Zumi7m80WE0o1dCGvMB5lbJwOkwG6S4H rdyPrJ44jFPodmKOZv/jYTPcA68RpjYsRDoCmiI2TY2b99aM+jx4yzlh2fXeamG9 GzgRjg8MfV76PiCUYDCwLLLqHupqghqCIvecNhFKYKp+lzp8j8eiESXrqSlAe23P DVJrNFFK2YFrrbB9WJOO =vcUF -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core perf/core improvements and fixes: - Preparatory patches to use hw events in PMU syntax, from Jiri Olsa - Remaining backport of trace-cmd's libparseevent, from Namhyung Kim - Fix libtraceevent 'clean' make target, from Namhyung Kim - Teach ctags about libtraceevent error codes, from Namhyung Kim - Fix libtraceevent dependency files usage, from Namhyung Kim - Support hex number pretty printing in libtraceevent, fixing kvm output, from Namhyung Kim - Kill some die() usage in libtraceevent, from Namhyung Kim - Improve support for hw breakpoints parsing/pretty printing/testing, from Jiri Olsa - Clarify perf bench option naming, from Hitoshi Mitake - Look for ".note" ELF notes too, used in the kernel vdso, from Jiri Olsa - Fix internal PMU list usage, removing leak, from Robert Richter Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Коммит
387ef4e24a
|
@ -250,8 +250,12 @@ endef
|
|||
all_objs := $(sort $(ALL_OBJS))
|
||||
all_deps := $(all_objs:%.o=.%.d)
|
||||
|
||||
# let .d file also depends on the source and header files
|
||||
define check_deps
|
||||
$(CC) -M $(CFLAGS) $< > $@;
|
||||
@set -e; $(RM) $@; \
|
||||
$(CC) -M $(CFLAGS) $< > $@.$$$$; \
|
||||
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
$(RM) $@.$$$$
|
||||
endef
|
||||
|
||||
$(gui_deps): ks_version.h
|
||||
|
@ -270,11 +274,13 @@ endif
|
|||
|
||||
tags: force
|
||||
$(RM) tags
|
||||
find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px
|
||||
find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
|
||||
--regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
|
||||
|
||||
TAGS: force
|
||||
$(RM) TAGS
|
||||
find . -name '*.[ch]' | xargs etags
|
||||
find . -name '*.[ch]' | xargs etags \
|
||||
--regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
|
||||
|
||||
define do_install
|
||||
$(print_install) \
|
||||
|
@ -290,7 +296,7 @@ install_lib: all_cmd install_plugins install_python
|
|||
install: install_lib
|
||||
|
||||
clean:
|
||||
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES).*.d
|
||||
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d
|
||||
$(RM) tags TAGS
|
||||
|
||||
endif # skip-makefile
|
||||
|
|
|
@ -467,8 +467,10 @@ int pevent_register_function(struct pevent *pevent, char *func,
|
|||
item->mod = NULL;
|
||||
item->addr = addr;
|
||||
|
||||
pevent->funclist = item;
|
||||
if (!item->func || (mod && !item->mod))
|
||||
die("malloc func");
|
||||
|
||||
pevent->funclist = item;
|
||||
pevent->func_count++;
|
||||
|
||||
return 0;
|
||||
|
@ -511,12 +513,12 @@ struct printk_list {
|
|||
|
||||
static int printk_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct func_map *fa = a;
|
||||
const struct func_map *fb = b;
|
||||
const struct printk_map *pa = a;
|
||||
const struct printk_map *pb = b;
|
||||
|
||||
if (fa->addr < fb->addr)
|
||||
if (pa->addr < pb->addr)
|
||||
return -1;
|
||||
if (fa->addr > fb->addr)
|
||||
if (pa->addr > pb->addr)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -583,10 +585,13 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt,
|
|||
item = malloc_or_die(sizeof(*item));
|
||||
|
||||
item->next = pevent->printklist;
|
||||
pevent->printklist = item;
|
||||
item->printk = strdup(fmt);
|
||||
item->addr = addr;
|
||||
|
||||
if (!item->printk)
|
||||
die("malloc fmt");
|
||||
|
||||
pevent->printklist = item;
|
||||
pevent->printk_count++;
|
||||
|
||||
return 0;
|
||||
|
@ -616,7 +621,9 @@ static struct event_format *alloc_event(void)
|
|||
{
|
||||
struct event_format *event;
|
||||
|
||||
event = malloc_or_die(sizeof(*event));
|
||||
event = malloc(sizeof(*event));
|
||||
if (!event)
|
||||
return NULL;
|
||||
memset(event, 0, sizeof(*event));
|
||||
|
||||
return event;
|
||||
|
@ -626,12 +633,8 @@ static void add_event(struct pevent *pevent, struct event_format *event)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!pevent->events)
|
||||
pevent->events = malloc_or_die(sizeof(event));
|
||||
else
|
||||
pevent->events =
|
||||
realloc(pevent->events, sizeof(event) *
|
||||
(pevent->nr_events + 1));
|
||||
pevent->events = realloc(pevent->events, sizeof(event) *
|
||||
(pevent->nr_events + 1));
|
||||
if (!pevent->events)
|
||||
die("Can not allocate events");
|
||||
|
||||
|
@ -697,6 +700,10 @@ static void free_arg(struct print_arg *arg)
|
|||
free_arg(arg->symbol.field);
|
||||
free_flag_sym(arg->symbol.symbols);
|
||||
break;
|
||||
case PRINT_HEX:
|
||||
free_arg(arg->hex.field);
|
||||
free_arg(arg->hex.size);
|
||||
break;
|
||||
case PRINT_TYPE:
|
||||
free(arg->typecast.type);
|
||||
free_arg(arg->typecast.item);
|
||||
|
@ -775,6 +782,25 @@ int pevent_peek_char(void)
|
|||
return __peek_char();
|
||||
}
|
||||
|
||||
static int extend_token(char **tok, char *buf, int size)
|
||||
{
|
||||
char *newtok = realloc(*tok, size);
|
||||
|
||||
if (!newtok) {
|
||||
free(*tok);
|
||||
*tok = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*tok)
|
||||
strcpy(newtok, buf);
|
||||
else
|
||||
strcat(newtok, buf);
|
||||
*tok = newtok;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum event_type force_token(const char *str, char **tok);
|
||||
|
||||
static enum event_type __read_token(char **tok)
|
||||
|
@ -859,17 +885,10 @@ static enum event_type __read_token(char **tok)
|
|||
do {
|
||||
if (i == (BUFSIZ - 1)) {
|
||||
buf[i] = 0;
|
||||
if (*tok) {
|
||||
*tok = realloc(*tok, tok_size + BUFSIZ);
|
||||
if (!*tok)
|
||||
return EVENT_NONE;
|
||||
strcat(*tok, buf);
|
||||
} else
|
||||
*tok = strdup(buf);
|
||||
|
||||
if (!*tok)
|
||||
return EVENT_NONE;
|
||||
tok_size += BUFSIZ;
|
||||
|
||||
if (extend_token(tok, buf, tok_size) < 0)
|
||||
return EVENT_NONE;
|
||||
i = 0;
|
||||
}
|
||||
last_ch = ch;
|
||||
|
@ -908,17 +927,10 @@ static enum event_type __read_token(char **tok)
|
|||
while (get_type(__peek_char()) == type) {
|
||||
if (i == (BUFSIZ - 1)) {
|
||||
buf[i] = 0;
|
||||
if (*tok) {
|
||||
*tok = realloc(*tok, tok_size + BUFSIZ);
|
||||
if (!*tok)
|
||||
return EVENT_NONE;
|
||||
strcat(*tok, buf);
|
||||
} else
|
||||
*tok = strdup(buf);
|
||||
|
||||
if (!*tok)
|
||||
return EVENT_NONE;
|
||||
tok_size += BUFSIZ;
|
||||
|
||||
if (extend_token(tok, buf, tok_size) < 0)
|
||||
return EVENT_NONE;
|
||||
i = 0;
|
||||
}
|
||||
ch = __read_char();
|
||||
|
@ -927,14 +939,7 @@ static enum event_type __read_token(char **tok)
|
|||
|
||||
out:
|
||||
buf[i] = 0;
|
||||
if (*tok) {
|
||||
*tok = realloc(*tok, tok_size + i);
|
||||
if (!*tok)
|
||||
return EVENT_NONE;
|
||||
strcat(*tok, buf);
|
||||
} else
|
||||
*tok = strdup(buf);
|
||||
if (!*tok)
|
||||
if (extend_token(tok, buf, tok_size + i + 1) < 0)
|
||||
return EVENT_NONE;
|
||||
|
||||
if (type == EVENT_ITEM) {
|
||||
|
@ -1255,9 +1260,15 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
field->flags |= FIELD_IS_POINTER;
|
||||
|
||||
if (field->type) {
|
||||
field->type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(last_token) + 2);
|
||||
char *new_type;
|
||||
new_type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(last_token) + 2);
|
||||
if (!new_type) {
|
||||
free(last_token);
|
||||
goto fail;
|
||||
}
|
||||
field->type = new_type;
|
||||
strcat(field->type, " ");
|
||||
strcat(field->type, last_token);
|
||||
free(last_token);
|
||||
|
@ -1282,6 +1293,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
if (strcmp(token, "[") == 0) {
|
||||
enum event_type last_type = type;
|
||||
char *brackets = token;
|
||||
char *new_brackets;
|
||||
int len;
|
||||
|
||||
field->flags |= FIELD_IS_ARRAY;
|
||||
|
@ -1301,9 +1313,14 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
len = 1;
|
||||
last_type = type;
|
||||
|
||||
brackets = realloc(brackets,
|
||||
strlen(brackets) +
|
||||
strlen(token) + len);
|
||||
new_brackets = realloc(brackets,
|
||||
strlen(brackets) +
|
||||
strlen(token) + len);
|
||||
if (!new_brackets) {
|
||||
free(brackets);
|
||||
goto fail;
|
||||
}
|
||||
brackets = new_brackets;
|
||||
if (len == 2)
|
||||
strcat(brackets, " ");
|
||||
strcat(brackets, token);
|
||||
|
@ -1319,7 +1336,12 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
|
||||
free_token(token);
|
||||
|
||||
brackets = realloc(brackets, strlen(brackets) + 2);
|
||||
new_brackets = realloc(brackets, strlen(brackets) + 2);
|
||||
if (!new_brackets) {
|
||||
free(brackets);
|
||||
goto fail;
|
||||
}
|
||||
brackets = new_brackets;
|
||||
strcat(brackets, "]");
|
||||
|
||||
/* add brackets to type */
|
||||
|
@ -1330,10 +1352,16 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
* the format: type [] item;
|
||||
*/
|
||||
if (type == EVENT_ITEM) {
|
||||
field->type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(field->name) +
|
||||
strlen(brackets) + 2);
|
||||
char *new_type;
|
||||
new_type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(field->name) +
|
||||
strlen(brackets) + 2);
|
||||
if (!new_type) {
|
||||
free(brackets);
|
||||
goto fail;
|
||||
}
|
||||
field->type = new_type;
|
||||
strcat(field->type, " ");
|
||||
strcat(field->type, field->name);
|
||||
free_token(field->name);
|
||||
|
@ -1341,9 +1369,15 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
field->name = token;
|
||||
type = read_token(&token);
|
||||
} else {
|
||||
field->type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(brackets) + 1);
|
||||
char *new_type;
|
||||
new_type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(brackets) + 1);
|
||||
if (!new_type) {
|
||||
free(brackets);
|
||||
goto fail;
|
||||
}
|
||||
field->type = new_type;
|
||||
strcat(field->type, brackets);
|
||||
}
|
||||
free(brackets);
|
||||
|
@ -1726,10 +1760,16 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
|
|||
/* could just be a type pointer */
|
||||
if ((strcmp(arg->op.op, "*") == 0) &&
|
||||
type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
|
||||
char *new_atom;
|
||||
|
||||
if (left->type != PRINT_ATOM)
|
||||
die("bad pointer type");
|
||||
left->atom.atom = realloc(left->atom.atom,
|
||||
new_atom = realloc(left->atom.atom,
|
||||
strlen(left->atom.atom) + 3);
|
||||
if (!new_atom)
|
||||
goto out_free;
|
||||
|
||||
left->atom.atom = new_atom;
|
||||
strcat(left->atom.atom, " *");
|
||||
free(arg->op.op);
|
||||
*arg = *left;
|
||||
|
@ -2146,6 +2186,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
|
|||
if (value == NULL)
|
||||
goto out_free;
|
||||
field->value = strdup(value);
|
||||
if (field->value == NULL)
|
||||
goto out_free;
|
||||
|
||||
free_arg(arg);
|
||||
arg = alloc_arg();
|
||||
|
@ -2159,6 +2201,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
|
|||
if (value == NULL)
|
||||
goto out_free;
|
||||
field->str = strdup(value);
|
||||
if (field->str == NULL)
|
||||
goto out_free;
|
||||
free_arg(arg);
|
||||
arg = NULL;
|
||||
|
||||
|
@ -2259,6 +2303,45 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
|
|||
return EVENT_ERROR;
|
||||
}
|
||||
|
||||
static enum event_type
|
||||
process_hex(struct event_format *event, struct print_arg *arg, char **tok)
|
||||
{
|
||||
struct print_arg *field;
|
||||
enum event_type type;
|
||||
char *token;
|
||||
|
||||
memset(arg, 0, sizeof(*arg));
|
||||
arg->type = PRINT_HEX;
|
||||
|
||||
field = alloc_arg();
|
||||
type = process_arg(event, field, &token);
|
||||
|
||||
if (test_type_token(type, token, EVENT_DELIM, ","))
|
||||
goto out_free;
|
||||
|
||||
arg->hex.field = field;
|
||||
|
||||
free_token(token);
|
||||
|
||||
field = alloc_arg();
|
||||
type = process_arg(event, field, &token);
|
||||
|
||||
if (test_type_token(type, token, EVENT_DELIM, ")"))
|
||||
goto out_free;
|
||||
|
||||
arg->hex.size = field;
|
||||
|
||||
free_token(token);
|
||||
type = read_token_item(tok);
|
||||
return type;
|
||||
|
||||
out_free:
|
||||
free_arg(field);
|
||||
free_token(token);
|
||||
*tok = NULL;
|
||||
return EVENT_ERROR;
|
||||
}
|
||||
|
||||
static enum event_type
|
||||
process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok)
|
||||
{
|
||||
|
@ -2488,6 +2571,10 @@ process_function(struct event_format *event, struct print_arg *arg,
|
|||
is_symbolic_field = 1;
|
||||
return process_symbols(event, arg, tok);
|
||||
}
|
||||
if (strcmp(token, "__print_hex") == 0) {
|
||||
free_token(token);
|
||||
return process_hex(event, arg, tok);
|
||||
}
|
||||
if (strcmp(token, "__get_str") == 0) {
|
||||
free_token(token);
|
||||
return process_str(event, arg, tok);
|
||||
|
@ -2541,7 +2628,16 @@ process_arg_token(struct event_format *event, struct print_arg *arg,
|
|||
}
|
||||
/* atoms can be more than one token long */
|
||||
while (type == EVENT_ITEM) {
|
||||
atom = realloc(atom, strlen(atom) + strlen(token) + 2);
|
||||
char *new_atom;
|
||||
new_atom = realloc(atom,
|
||||
strlen(atom) + strlen(token) + 2);
|
||||
if (!new_atom) {
|
||||
free(atom);
|
||||
*tok = NULL;
|
||||
free_token(token);
|
||||
return EVENT_ERROR;
|
||||
}
|
||||
atom = new_atom;
|
||||
strcat(atom, " ");
|
||||
strcat(atom, token);
|
||||
free_token(token);
|
||||
|
@ -2835,7 +2931,7 @@ static int get_common_info(struct pevent *pevent,
|
|||
event = pevent->events[0];
|
||||
field = pevent_find_common_field(event, type);
|
||||
if (!field)
|
||||
die("field '%s' not found", type);
|
||||
return -1;
|
||||
|
||||
*offset = field->offset;
|
||||
*size = field->size;
|
||||
|
@ -2886,15 +2982,16 @@ static int parse_common_flags(struct pevent *pevent, void *data)
|
|||
|
||||
static int parse_common_lock_depth(struct pevent *pevent, void *data)
|
||||
{
|
||||
int ret;
|
||||
return __parse_common(pevent, data,
|
||||
&pevent->ld_size, &pevent->ld_offset,
|
||||
"common_lock_depth");
|
||||
}
|
||||
|
||||
ret = __parse_common(pevent, data,
|
||||
&pevent->ld_size, &pevent->ld_offset,
|
||||
"common_lock_depth");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
static int parse_common_migrate_disable(struct pevent *pevent, void *data)
|
||||
{
|
||||
return __parse_common(pevent, data,
|
||||
&pevent->ld_size, &pevent->ld_offset,
|
||||
"common_migrate_disable");
|
||||
}
|
||||
|
||||
static int events_id_cmp(const void *a, const void *b);
|
||||
|
@ -2995,6 +3092,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
|
|||
break;
|
||||
case PRINT_FLAGS:
|
||||
case PRINT_SYMBOL:
|
||||
case PRINT_HEX:
|
||||
break;
|
||||
case PRINT_TYPE:
|
||||
val = eval_num_arg(data, size, event, arg->typecast.item);
|
||||
|
@ -3214,11 +3312,13 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
|||
{
|
||||
struct pevent *pevent = event->pevent;
|
||||
struct print_flag_sym *flag;
|
||||
struct format_field *field;
|
||||
unsigned long long val, fval;
|
||||
unsigned long addr;
|
||||
char *str;
|
||||
unsigned char *hex;
|
||||
int print;
|
||||
int len;
|
||||
int i, len;
|
||||
|
||||
switch (arg->type) {
|
||||
case PRINT_NULL:
|
||||
|
@ -3228,27 +3328,29 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
|||
print_str_to_seq(s, format, len_arg, arg->atom.atom);
|
||||
return;
|
||||
case PRINT_FIELD:
|
||||
if (!arg->field.field) {
|
||||
arg->field.field = pevent_find_any_field(event, arg->field.name);
|
||||
if (!arg->field.field)
|
||||
field = arg->field.field;
|
||||
if (!field) {
|
||||
field = pevent_find_any_field(event, arg->field.name);
|
||||
if (!field)
|
||||
die("field %s not found", arg->field.name);
|
||||
arg->field.field = field;
|
||||
}
|
||||
/* Zero sized fields, mean the rest of the data */
|
||||
len = arg->field.field->size ? : size - arg->field.field->offset;
|
||||
len = field->size ? : size - field->offset;
|
||||
|
||||
/*
|
||||
* Some events pass in pointers. If this is not an array
|
||||
* and the size is the same as long_size, assume that it
|
||||
* is a pointer.
|
||||
*/
|
||||
if (!(arg->field.field->flags & FIELD_IS_ARRAY) &&
|
||||
arg->field.field->size == pevent->long_size) {
|
||||
addr = *(unsigned long *)(data + arg->field.field->offset);
|
||||
if (!(field->flags & FIELD_IS_ARRAY) &&
|
||||
field->size == pevent->long_size) {
|
||||
addr = *(unsigned long *)(data + field->offset);
|
||||
trace_seq_printf(s, "%lx", addr);
|
||||
break;
|
||||
}
|
||||
str = malloc_or_die(len + 1);
|
||||
memcpy(str, data + arg->field.field->offset, len);
|
||||
memcpy(str, data + field->offset, len);
|
||||
str[len] = 0;
|
||||
print_str_to_seq(s, format, len_arg, str);
|
||||
free(str);
|
||||
|
@ -3281,6 +3383,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
|||
}
|
||||
}
|
||||
break;
|
||||
case PRINT_HEX:
|
||||
field = arg->hex.field->field.field;
|
||||
if (!field) {
|
||||
str = arg->hex.field->field.name;
|
||||
field = pevent_find_any_field(event, str);
|
||||
if (!field)
|
||||
die("field %s not found", str);
|
||||
arg->hex.field->field.field = field;
|
||||
}
|
||||
hex = data + field->offset;
|
||||
len = eval_num_arg(data, size, event, arg->hex.size);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i)
|
||||
trace_seq_putc(s, ' ');
|
||||
trace_seq_printf(s, "%02x", hex[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PRINT_TYPE:
|
||||
break;
|
||||
|
@ -3299,7 +3418,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
|||
break;
|
||||
}
|
||||
case PRINT_BSTRING:
|
||||
trace_seq_printf(s, format, arg->string.string);
|
||||
print_str_to_seq(s, format, len_arg, arg->string.string);
|
||||
break;
|
||||
case PRINT_OP:
|
||||
/*
|
||||
|
@ -3363,6 +3482,10 @@ process_defined_func(struct trace_seq *s, void *data, int size,
|
|||
string = malloc_or_die(sizeof(*string));
|
||||
string->next = strings;
|
||||
string->str = strdup(str.buffer);
|
||||
if (!string->str)
|
||||
die("malloc str");
|
||||
|
||||
args[i] = (unsigned long long)string->str;
|
||||
strings = string;
|
||||
trace_seq_destroy(&str);
|
||||
break;
|
||||
|
@ -3400,6 +3523,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
|||
unsigned long long ip, val;
|
||||
char *ptr;
|
||||
void *bptr;
|
||||
int vsize;
|
||||
|
||||
field = pevent->bprint_buf_field;
|
||||
ip_field = pevent->bprint_ip_field;
|
||||
|
@ -3448,6 +3572,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
|||
goto process_again;
|
||||
case '0' ... '9':
|
||||
goto process_again;
|
||||
case '.':
|
||||
goto process_again;
|
||||
case 'p':
|
||||
ls = 1;
|
||||
/* fall through */
|
||||
|
@ -3455,23 +3581,30 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
|||
case 'u':
|
||||
case 'x':
|
||||
case 'i':
|
||||
switch (ls) {
|
||||
case 0:
|
||||
vsize = 4;
|
||||
break;
|
||||
case 1:
|
||||
vsize = pevent->long_size;
|
||||
break;
|
||||
case 2:
|
||||
vsize = 8;
|
||||
break;
|
||||
default:
|
||||
vsize = ls; /* ? */
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case '*':
|
||||
if (*ptr == '*')
|
||||
vsize = 4;
|
||||
|
||||
/* the pointers are always 4 bytes aligned */
|
||||
bptr = (void *)(((unsigned long)bptr + 3) &
|
||||
~3);
|
||||
switch (ls) {
|
||||
case 0:
|
||||
ls = 4;
|
||||
break;
|
||||
case 1:
|
||||
ls = pevent->long_size;
|
||||
break;
|
||||
case 2:
|
||||
ls = 8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
val = pevent_read_number(pevent, bptr, ls);
|
||||
bptr += ls;
|
||||
val = pevent_read_number(pevent, bptr, vsize);
|
||||
bptr += vsize;
|
||||
arg = alloc_arg();
|
||||
arg->next = NULL;
|
||||
arg->type = PRINT_ATOM;
|
||||
|
@ -3479,12 +3612,21 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
|||
sprintf(arg->atom.atom, "%lld", val);
|
||||
*next = arg;
|
||||
next = &arg->next;
|
||||
/*
|
||||
* The '*' case means that an arg is used as the length.
|
||||
* We need to continue to figure out for what.
|
||||
*/
|
||||
if (*ptr == '*')
|
||||
goto process_again;
|
||||
|
||||
break;
|
||||
case 's':
|
||||
arg = alloc_arg();
|
||||
arg->next = NULL;
|
||||
arg->type = PRINT_BSTRING;
|
||||
arg->string.string = strdup(bptr);
|
||||
if (!arg->string.string)
|
||||
break;
|
||||
bptr += strlen(bptr) + 1;
|
||||
*next = arg;
|
||||
next = &arg->next;
|
||||
|
@ -3589,6 +3731,16 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
|
|||
trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
|
||||
}
|
||||
|
||||
static int is_printable_array(char *p, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len && p[i]; i++)
|
||||
if (!isprint(p[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_event_fields(struct trace_seq *s, void *data, int size,
|
||||
struct event_format *event)
|
||||
{
|
||||
|
@ -3608,7 +3760,8 @@ static void print_event_fields(struct trace_seq *s, void *data, int size,
|
|||
len = offset >> 16;
|
||||
offset &= 0xffff;
|
||||
}
|
||||
if (field->flags & FIELD_IS_STRING) {
|
||||
if (field->flags & FIELD_IS_STRING &&
|
||||
is_printable_array(data + offset, len)) {
|
||||
trace_seq_printf(s, "%s", (char *)data + offset);
|
||||
} else {
|
||||
trace_seq_puts(s, "ARRAY[");
|
||||
|
@ -3619,6 +3772,7 @@ static void print_event_fields(struct trace_seq *s, void *data, int size,
|
|||
*((unsigned char *)data + offset + i));
|
||||
}
|
||||
trace_seq_putc(s, ']');
|
||||
field->flags &= ~FIELD_IS_STRING;
|
||||
}
|
||||
} else {
|
||||
val = pevent_read_number(event->pevent, data + field->offset,
|
||||
|
@ -3758,6 +3912,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
|
|||
} else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') {
|
||||
print_mac_arg(s, *(ptr+1), data, size, event, arg);
|
||||
ptr++;
|
||||
arg = arg->next;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3794,14 +3949,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (pevent->long_size == 8 && ls) {
|
||||
if (pevent->long_size == 8 && ls &&
|
||||
sizeof(long) != 8) {
|
||||
char *p;
|
||||
|
||||
ls = 2;
|
||||
/* make %l into %ll */
|
||||
p = strchr(format, 'l');
|
||||
if (p)
|
||||
memmove(p, p+1, strlen(p)+1);
|
||||
memmove(p+1, p, strlen(p)+1);
|
||||
else if (strcmp(format, "%p") == 0)
|
||||
strcpy(format, "0x%llx");
|
||||
}
|
||||
|
@ -3878,8 +4034,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
|
|||
* pevent_data_lat_fmt - parse the data for the latency format
|
||||
* @pevent: a handle to the pevent
|
||||
* @s: the trace_seq to write to
|
||||
* @data: the raw data to read from
|
||||
* @size: currently unused.
|
||||
* @record: the record to read from
|
||||
*
|
||||
* This parses out the Latency format (interrupts disabled,
|
||||
* need rescheduling, in hard/soft interrupt, preempt count
|
||||
|
@ -3889,10 +4044,13 @@ void pevent_data_lat_fmt(struct pevent *pevent,
|
|||
struct trace_seq *s, struct pevent_record *record)
|
||||
{
|
||||
static int check_lock_depth = 1;
|
||||
static int check_migrate_disable = 1;
|
||||
static int lock_depth_exists;
|
||||
static int migrate_disable_exists;
|
||||
unsigned int lat_flags;
|
||||
unsigned int pc;
|
||||
int lock_depth;
|
||||
int migrate_disable;
|
||||
int hardirq;
|
||||
int softirq;
|
||||
void *data = record->data;
|
||||
|
@ -3900,18 +4058,26 @@ void pevent_data_lat_fmt(struct pevent *pevent,
|
|||
lat_flags = parse_common_flags(pevent, data);
|
||||
pc = parse_common_pc(pevent, data);
|
||||
/* lock_depth may not always exist */
|
||||
if (check_lock_depth) {
|
||||
struct format_field *field;
|
||||
struct event_format *event;
|
||||
|
||||
check_lock_depth = 0;
|
||||
event = pevent->events[0];
|
||||
field = pevent_find_common_field(event, "common_lock_depth");
|
||||
if (field)
|
||||
lock_depth_exists = 1;
|
||||
}
|
||||
if (lock_depth_exists)
|
||||
lock_depth = parse_common_lock_depth(pevent, data);
|
||||
else if (check_lock_depth) {
|
||||
lock_depth = parse_common_lock_depth(pevent, data);
|
||||
if (lock_depth < 0)
|
||||
check_lock_depth = 0;
|
||||
else
|
||||
lock_depth_exists = 1;
|
||||
}
|
||||
|
||||
/* migrate_disable may not always exist */
|
||||
if (migrate_disable_exists)
|
||||
migrate_disable = parse_common_migrate_disable(pevent, data);
|
||||
else if (check_migrate_disable) {
|
||||
migrate_disable = parse_common_migrate_disable(pevent, data);
|
||||
if (migrate_disable < 0)
|
||||
check_migrate_disable = 0;
|
||||
else
|
||||
migrate_disable_exists = 1;
|
||||
}
|
||||
|
||||
hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
|
||||
softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
|
||||
|
@ -3930,6 +4096,13 @@ void pevent_data_lat_fmt(struct pevent *pevent,
|
|||
else
|
||||
trace_seq_putc(s, '.');
|
||||
|
||||
if (migrate_disable_exists) {
|
||||
if (migrate_disable < 0)
|
||||
trace_seq_putc(s, '.');
|
||||
else
|
||||
trace_seq_printf(s, "%d", migrate_disable);
|
||||
}
|
||||
|
||||
if (lock_depth_exists) {
|
||||
if (lock_depth < 0)
|
||||
trace_seq_putc(s, '.');
|
||||
|
@ -3996,10 +4169,7 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid)
|
|||
* pevent_data_comm_from_pid - parse the data into the print format
|
||||
* @s: the trace_seq to write to
|
||||
* @event: the handle to the event
|
||||
* @cpu: the cpu the event was recorded on
|
||||
* @data: the raw data
|
||||
* @size: the size of the raw data
|
||||
* @nsecs: the timestamp of the event
|
||||
* @record: the record to read from
|
||||
*
|
||||
* This parses the raw @data using the given @event information and
|
||||
* writes the print format into the trace_seq.
|
||||
|
@ -4279,6 +4449,13 @@ static void print_args(struct print_arg *args)
|
|||
trace_seq_destroy(&s);
|
||||
printf(")");
|
||||
break;
|
||||
case PRINT_HEX:
|
||||
printf("__print_hex(");
|
||||
print_args(args->hex.field);
|
||||
printf(", ");
|
||||
print_args(args->hex.size);
|
||||
printf(")");
|
||||
break;
|
||||
case PRINT_STRING:
|
||||
case PRINT_BSTRING:
|
||||
printf("__get_str(%s)", args->string.string);
|
||||
|
@ -4541,6 +4718,8 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
die("failed to read event id");
|
||||
|
||||
event->system = strdup(sys);
|
||||
if (!event->system)
|
||||
die("failed to allocate system");
|
||||
|
||||
/* Add pevent to event so that it can be referenced */
|
||||
event->pevent = pevent;
|
||||
|
@ -4582,6 +4761,11 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
list = &arg->next;
|
||||
arg->type = PRINT_FIELD;
|
||||
arg->field.name = strdup(field->name);
|
||||
if (!arg->field.name) {
|
||||
do_warning("failed to allocate field name");
|
||||
event->flags |= EVENT_FL_FAILED;
|
||||
return -1;
|
||||
}
|
||||
arg->field.field = field;
|
||||
}
|
||||
return 0;
|
||||
|
@ -4753,7 +4937,7 @@ int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event,
|
|||
* @record: The record with the field name.
|
||||
* @err: print default error if failed.
|
||||
*
|
||||
* Returns: 0 on success, -1 field not fould, or 1 if buffer is full.
|
||||
* Returns: 0 on success, -1 field not found, or 1 if buffer is full.
|
||||
*/
|
||||
int pevent_print_num_field(struct trace_seq *s, const char *fmt,
|
||||
struct event_format *event, const char *name,
|
||||
|
@ -4795,11 +4979,12 @@ static void free_func_handle(struct pevent_function_handler *func)
|
|||
* pevent_register_print_function - register a helper function
|
||||
* @pevent: the handle to the pevent
|
||||
* @func: the function to process the helper function
|
||||
* @ret_type: the return type of the helper function
|
||||
* @name: the name of the helper function
|
||||
* @parameters: A list of enum pevent_func_arg_type
|
||||
*
|
||||
* Some events may have helper functions in the print format arguments.
|
||||
* This allows a plugin to dynmically create a way to process one
|
||||
* This allows a plugin to dynamically create a way to process one
|
||||
* of these functions.
|
||||
*
|
||||
* The @parameters is a variable list of pevent_func_arg_type enums that
|
||||
|
@ -4870,12 +5055,13 @@ int pevent_register_print_function(struct pevent *pevent,
|
|||
}
|
||||
|
||||
/**
|
||||
* pevent_register_event_handle - register a way to parse an event
|
||||
* pevent_register_event_handler - register a way to parse an event
|
||||
* @pevent: the handle to the pevent
|
||||
* @id: the id of the event to register
|
||||
* @sys_name: the system name the event belongs to
|
||||
* @event_name: the name of the event
|
||||
* @func: the function to call to parse the event information
|
||||
* @context: the data to be passed to @func
|
||||
*
|
||||
* This function allows a developer to override the parsing of
|
||||
* a given event. If for some reason the default print format
|
||||
|
@ -4925,6 +5111,11 @@ int pevent_register_event_handler(struct pevent *pevent,
|
|||
if (sys_name)
|
||||
handle->sys_name = strdup(sys_name);
|
||||
|
||||
if ((event_name && !handle->event_name) ||
|
||||
(sys_name && !handle->sys_name)) {
|
||||
die("Failed to allocate event/sys name");
|
||||
}
|
||||
|
||||
handle->func = func;
|
||||
handle->next = pevent->handlers;
|
||||
pevent->handlers = handle;
|
||||
|
|
|
@ -226,6 +226,11 @@ struct print_arg_symbol {
|
|||
struct print_flag_sym *symbols;
|
||||
};
|
||||
|
||||
struct print_arg_hex {
|
||||
struct print_arg *field;
|
||||
struct print_arg *size;
|
||||
};
|
||||
|
||||
struct print_arg_dynarray {
|
||||
struct format_field *field;
|
||||
struct print_arg *index;
|
||||
|
@ -253,6 +258,7 @@ enum print_arg_type {
|
|||
PRINT_FIELD,
|
||||
PRINT_FLAGS,
|
||||
PRINT_SYMBOL,
|
||||
PRINT_HEX,
|
||||
PRINT_TYPE,
|
||||
PRINT_STRING,
|
||||
PRINT_BSTRING,
|
||||
|
@ -270,6 +276,7 @@ struct print_arg {
|
|||
struct print_arg_typecast typecast;
|
||||
struct print_arg_flags flags;
|
||||
struct print_arg_symbol symbol;
|
||||
struct print_arg_hex hex;
|
||||
struct print_arg_func func;
|
||||
struct print_arg_string string;
|
||||
struct print_arg_op op;
|
||||
|
|
|
@ -96,7 +96,7 @@ static enum event_type read_token(char **tok)
|
|||
(strcmp(token, "=") == 0 || strcmp(token, "!") == 0) &&
|
||||
pevent_peek_char() == '~') {
|
||||
/* append it */
|
||||
*tok = malloc(3);
|
||||
*tok = malloc_or_die(3);
|
||||
sprintf(*tok, "%c%c", *token, '~');
|
||||
free_token(token);
|
||||
/* Now remove the '~' from the buffer */
|
||||
|
@ -148,17 +148,11 @@ add_filter_type(struct event_filter *filter, int id)
|
|||
if (filter_type)
|
||||
return filter_type;
|
||||
|
||||
if (!filter->filters)
|
||||
filter->event_filters =
|
||||
malloc_or_die(sizeof(*filter->event_filters));
|
||||
else {
|
||||
filter->event_filters =
|
||||
realloc(filter->event_filters,
|
||||
sizeof(*filter->event_filters) *
|
||||
(filter->filters + 1));
|
||||
if (!filter->event_filters)
|
||||
die("Could not allocate filter");
|
||||
}
|
||||
filter->event_filters = realloc(filter->event_filters,
|
||||
sizeof(*filter->event_filters) *
|
||||
(filter->filters + 1));
|
||||
if (!filter->event_filters)
|
||||
die("Could not allocate filter");
|
||||
|
||||
for (i = 0; i < filter->filters; i++) {
|
||||
if (filter->event_filters[i].event_id > id)
|
||||
|
@ -1480,7 +1474,7 @@ void pevent_filter_clear_trivial(struct event_filter *filter,
|
|||
{
|
||||
struct filter_type *filter_type;
|
||||
int count = 0;
|
||||
int *ids;
|
||||
int *ids = NULL;
|
||||
int i;
|
||||
|
||||
if (!filter->filters)
|
||||
|
@ -1504,10 +1498,8 @@ void pevent_filter_clear_trivial(struct event_filter *filter,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (count)
|
||||
ids = realloc(ids, sizeof(*ids) * (count + 1));
|
||||
else
|
||||
ids = malloc(sizeof(*ids));
|
||||
|
||||
ids = realloc(ids, sizeof(*ids) * (count + 1));
|
||||
if (!ids)
|
||||
die("Can't allocate ids");
|
||||
ids[count++] = filter_type->event_id;
|
||||
|
@ -1710,18 +1702,43 @@ static int test_num(struct event_format *event,
|
|||
|
||||
static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record)
|
||||
{
|
||||
const char *val = record->data + arg->str.field->offset;
|
||||
struct event_format *event;
|
||||
struct pevent *pevent;
|
||||
unsigned long long addr;
|
||||
const char *val = NULL;
|
||||
char hex[64];
|
||||
|
||||
/*
|
||||
* We need to copy the data since we can't be sure the field
|
||||
* is null terminated.
|
||||
*/
|
||||
if (*(val + arg->str.field->size - 1)) {
|
||||
/* copy it */
|
||||
memcpy(arg->str.buffer, val, arg->str.field->size);
|
||||
/* the buffer is already NULL terminated */
|
||||
val = arg->str.buffer;
|
||||
/* If the field is not a string convert it */
|
||||
if (arg->str.field->flags & FIELD_IS_STRING) {
|
||||
val = record->data + arg->str.field->offset;
|
||||
|
||||
/*
|
||||
* We need to copy the data since we can't be sure the field
|
||||
* is null terminated.
|
||||
*/
|
||||
if (*(val + arg->str.field->size - 1)) {
|
||||
/* copy it */
|
||||
memcpy(arg->str.buffer, val, arg->str.field->size);
|
||||
/* the buffer is already NULL terminated */
|
||||
val = arg->str.buffer;
|
||||
}
|
||||
|
||||
} else {
|
||||
event = arg->str.field->event;
|
||||
pevent = event->pevent;
|
||||
addr = get_value(event, arg->str.field, record);
|
||||
|
||||
if (arg->str.field->flags & (FIELD_IS_POINTER | FIELD_IS_LONG))
|
||||
/* convert to a kernel symbol */
|
||||
val = pevent_find_function(pevent, addr);
|
||||
|
||||
if (val == NULL) {
|
||||
/* just use the hex of the string name */
|
||||
snprintf(hex, 64, "0x%llx", addr);
|
||||
val = hex;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
@ -2001,11 +2018,13 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg)
|
|||
char *lstr;
|
||||
char *rstr;
|
||||
char *op;
|
||||
char *str;
|
||||
char *str = NULL;
|
||||
int len;
|
||||
|
||||
lstr = arg_to_str(filter, arg->exp.left);
|
||||
rstr = arg_to_str(filter, arg->exp.right);
|
||||
if (!lstr || !rstr)
|
||||
goto out;
|
||||
|
||||
switch (arg->exp.type) {
|
||||
case FILTER_EXP_ADD:
|
||||
|
@ -2045,6 +2064,7 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg)
|
|||
len = strlen(op) + strlen(lstr) + strlen(rstr) + 4;
|
||||
str = malloc_or_die(len);
|
||||
snprintf(str, len, "%s %s %s", lstr, op, rstr);
|
||||
out:
|
||||
free(lstr);
|
||||
free(rstr);
|
||||
|
||||
|
@ -2061,6 +2081,8 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg)
|
|||
|
||||
lstr = arg_to_str(filter, arg->num.left);
|
||||
rstr = arg_to_str(filter, arg->num.right);
|
||||
if (!lstr || !rstr)
|
||||
goto out;
|
||||
|
||||
switch (arg->num.type) {
|
||||
case FILTER_CMP_EQ:
|
||||
|
@ -2097,6 +2119,7 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg)
|
|||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(lstr);
|
||||
free(rstr);
|
||||
return str;
|
||||
|
@ -2247,7 +2270,12 @@ int pevent_filter_compare(struct event_filter *filter1, struct event_filter *fil
|
|||
/* The best way to compare complex filters is with strings */
|
||||
str1 = arg_to_str(filter1, filter_type1->filter);
|
||||
str2 = arg_to_str(filter2, filter_type2->filter);
|
||||
result = strcmp(str1, str2) != 0;
|
||||
if (str1 && str2)
|
||||
result = strcmp(str1, str2) != 0;
|
||||
else
|
||||
/* bail out if allocation fails */
|
||||
result = 1;
|
||||
|
||||
free(str1);
|
||||
free(str2);
|
||||
if (result)
|
||||
|
|
|
@ -144,7 +144,7 @@ On x86-64, x86-64-unrolled, x86-64-movsq and x86-64-movsb are supported.
|
|||
Repeat memcpy invocation this number of times.
|
||||
|
||||
-c::
|
||||
--clock::
|
||||
--cycle::
|
||||
Use perf's cpu-cycles event instead of gettimeofday syscall.
|
||||
|
||||
-o::
|
||||
|
@ -176,7 +176,7 @@ On x86-64, x86-64-unrolled, x86-64-stosq and x86-64-stosb are supported.
|
|||
Repeat memset invocation this number of times.
|
||||
|
||||
-c::
|
||||
--clock::
|
||||
--cycle::
|
||||
Use perf's cpu-cycles event instead of gettimeofday syscall.
|
||||
|
||||
-o::
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
static const char *length_str = "1MB";
|
||||
static const char *routine = "default";
|
||||
static int iterations = 1;
|
||||
static bool use_clock;
|
||||
static int clock_fd;
|
||||
static bool use_cycle;
|
||||
static int cycle_fd;
|
||||
static bool only_prefault;
|
||||
static bool no_prefault;
|
||||
|
||||
|
@ -37,7 +37,7 @@ static const struct option options[] = {
|
|||
"Specify routine to copy"),
|
||||
OPT_INTEGER('i', "iterations", &iterations,
|
||||
"repeat memcpy() invocation this number of times"),
|
||||
OPT_BOOLEAN('c', "clock", &use_clock,
|
||||
OPT_BOOLEAN('c', "cycle", &use_cycle,
|
||||
"Use cycles event instead of gettimeofday() for measuring"),
|
||||
OPT_BOOLEAN('o', "only-prefault", &only_prefault,
|
||||
"Show only the result with page faults before memcpy()"),
|
||||
|
@ -76,27 +76,27 @@ static const char * const bench_mem_memcpy_usage[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct perf_event_attr clock_attr = {
|
||||
static struct perf_event_attr cycle_attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES
|
||||
};
|
||||
|
||||
static void init_clock(void)
|
||||
static void init_cycle(void)
|
||||
{
|
||||
clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0);
|
||||
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
|
||||
|
||||
if (clock_fd < 0 && errno == ENOSYS)
|
||||
if (cycle_fd < 0 && errno == ENOSYS)
|
||||
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
else
|
||||
BUG_ON(clock_fd < 0);
|
||||
BUG_ON(cycle_fd < 0);
|
||||
}
|
||||
|
||||
static u64 get_clock(void)
|
||||
static u64 get_cycle(void)
|
||||
{
|
||||
int ret;
|
||||
u64 clk;
|
||||
|
||||
ret = read(clock_fd, &clk, sizeof(u64));
|
||||
ret = read(cycle_fd, &clk, sizeof(u64));
|
||||
BUG_ON(ret != sizeof(u64));
|
||||
|
||||
return clk;
|
||||
|
@ -119,9 +119,9 @@ static void alloc_mem(void **dst, void **src, size_t length)
|
|||
die("memory allocation failed - maybe length is too large?\n");
|
||||
}
|
||||
|
||||
static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
|
||||
static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault)
|
||||
{
|
||||
u64 clock_start = 0ULL, clock_end = 0ULL;
|
||||
u64 cycle_start = 0ULL, cycle_end = 0ULL;
|
||||
void *src = NULL, *dst = NULL;
|
||||
int i;
|
||||
|
||||
|
@ -130,14 +130,14 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
|
|||
if (prefault)
|
||||
fn(dst, src, len);
|
||||
|
||||
clock_start = get_clock();
|
||||
cycle_start = get_cycle();
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, src, len);
|
||||
clock_end = get_clock();
|
||||
cycle_end = get_cycle();
|
||||
|
||||
free(src);
|
||||
free(dst);
|
||||
return clock_end - clock_start;
|
||||
return cycle_end - cycle_start;
|
||||
}
|
||||
|
||||
static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
|
||||
|
@ -182,17 +182,17 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
int i;
|
||||
size_t len;
|
||||
double result_bps[2];
|
||||
u64 result_clock[2];
|
||||
u64 result_cycle[2];
|
||||
|
||||
argc = parse_options(argc, argv, options,
|
||||
bench_mem_memcpy_usage, 0);
|
||||
|
||||
if (use_clock)
|
||||
init_clock();
|
||||
if (use_cycle)
|
||||
init_cycle();
|
||||
|
||||
len = (size_t)perf_atoll((char *)length_str);
|
||||
|
||||
result_clock[0] = result_clock[1] = 0ULL;
|
||||
result_cycle[0] = result_cycle[1] = 0ULL;
|
||||
result_bps[0] = result_bps[1] = 0.0;
|
||||
|
||||
if ((s64)len <= 0) {
|
||||
|
@ -223,11 +223,11 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
|
||||
if (!only_prefault && !no_prefault) {
|
||||
/* show both of results */
|
||||
if (use_clock) {
|
||||
result_clock[0] =
|
||||
do_memcpy_clock(routines[i].fn, len, false);
|
||||
result_clock[1] =
|
||||
do_memcpy_clock(routines[i].fn, len, true);
|
||||
if (use_cycle) {
|
||||
result_cycle[0] =
|
||||
do_memcpy_cycle(routines[i].fn, len, false);
|
||||
result_cycle[1] =
|
||||
do_memcpy_cycle(routines[i].fn, len, true);
|
||||
} else {
|
||||
result_bps[0] =
|
||||
do_memcpy_gettimeofday(routines[i].fn,
|
||||
|
@ -237,9 +237,9 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
len, true);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
result_clock[pf] =
|
||||
do_memcpy_clock(routines[i].fn,
|
||||
if (use_cycle) {
|
||||
result_cycle[pf] =
|
||||
do_memcpy_cycle(routines[i].fn,
|
||||
len, only_prefault);
|
||||
} else {
|
||||
result_bps[pf] =
|
||||
|
@ -251,12 +251,12 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte\n",
|
||||
(double)result_clock[0]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte\n",
|
||||
(double)result_cycle[0]
|
||||
/ (double)len);
|
||||
printf(" %14lf Clock/Byte (with prefault)\n",
|
||||
(double)result_clock[1]
|
||||
printf(" %14lf Cycle/Byte (with prefault)\n",
|
||||
(double)result_cycle[1]
|
||||
/ (double)len);
|
||||
} else {
|
||||
print_bps(result_bps[0]);
|
||||
|
@ -265,9 +265,9 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
printf(" (with prefault)\n");
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte",
|
||||
(double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte",
|
||||
(double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
print_bps(result_bps[pf]);
|
||||
|
@ -277,17 +277,17 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
break;
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
if (use_cycle) {
|
||||
printf("%lf %lf\n",
|
||||
(double)result_clock[0] / (double)len,
|
||||
(double)result_clock[1] / (double)len);
|
||||
(double)result_cycle[0] / (double)len,
|
||||
(double)result_cycle[1] / (double)len);
|
||||
} else {
|
||||
printf("%lf %lf\n",
|
||||
result_bps[0], result_bps[1]);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf("%lf\n", (double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf("%lf\n", (double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
printf("%lf\n", result_bps[pf]);
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
static const char *length_str = "1MB";
|
||||
static const char *routine = "default";
|
||||
static int iterations = 1;
|
||||
static bool use_clock;
|
||||
static int clock_fd;
|
||||
static bool use_cycle;
|
||||
static int cycle_fd;
|
||||
static bool only_prefault;
|
||||
static bool no_prefault;
|
||||
|
||||
|
@ -37,7 +37,7 @@ static const struct option options[] = {
|
|||
"Specify routine to set"),
|
||||
OPT_INTEGER('i', "iterations", &iterations,
|
||||
"repeat memset() invocation this number of times"),
|
||||
OPT_BOOLEAN('c', "clock", &use_clock,
|
||||
OPT_BOOLEAN('c', "cycle", &use_cycle,
|
||||
"Use cycles event instead of gettimeofday() for measuring"),
|
||||
OPT_BOOLEAN('o', "only-prefault", &only_prefault,
|
||||
"Show only the result with page faults before memset()"),
|
||||
|
@ -76,27 +76,27 @@ static const char * const bench_mem_memset_usage[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct perf_event_attr clock_attr = {
|
||||
static struct perf_event_attr cycle_attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES
|
||||
};
|
||||
|
||||
static void init_clock(void)
|
||||
static void init_cycle(void)
|
||||
{
|
||||
clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0);
|
||||
cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
|
||||
|
||||
if (clock_fd < 0 && errno == ENOSYS)
|
||||
if (cycle_fd < 0 && errno == ENOSYS)
|
||||
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
else
|
||||
BUG_ON(clock_fd < 0);
|
||||
BUG_ON(cycle_fd < 0);
|
||||
}
|
||||
|
||||
static u64 get_clock(void)
|
||||
static u64 get_cycle(void)
|
||||
{
|
||||
int ret;
|
||||
u64 clk;
|
||||
|
||||
ret = read(clock_fd, &clk, sizeof(u64));
|
||||
ret = read(cycle_fd, &clk, sizeof(u64));
|
||||
BUG_ON(ret != sizeof(u64));
|
||||
|
||||
return clk;
|
||||
|
@ -115,9 +115,9 @@ static void alloc_mem(void **dst, size_t length)
|
|||
die("memory allocation failed - maybe length is too large?\n");
|
||||
}
|
||||
|
||||
static u64 do_memset_clock(memset_t fn, size_t len, bool prefault)
|
||||
static u64 do_memset_cycle(memset_t fn, size_t len, bool prefault)
|
||||
{
|
||||
u64 clock_start = 0ULL, clock_end = 0ULL;
|
||||
u64 cycle_start = 0ULL, cycle_end = 0ULL;
|
||||
void *dst = NULL;
|
||||
int i;
|
||||
|
||||
|
@ -126,13 +126,13 @@ static u64 do_memset_clock(memset_t fn, size_t len, bool prefault)
|
|||
if (prefault)
|
||||
fn(dst, -1, len);
|
||||
|
||||
clock_start = get_clock();
|
||||
cycle_start = get_cycle();
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, i, len);
|
||||
clock_end = get_clock();
|
||||
cycle_end = get_cycle();
|
||||
|
||||
free(dst);
|
||||
return clock_end - clock_start;
|
||||
return cycle_end - cycle_start;
|
||||
}
|
||||
|
||||
static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault)
|
||||
|
@ -176,17 +176,17 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
int i;
|
||||
size_t len;
|
||||
double result_bps[2];
|
||||
u64 result_clock[2];
|
||||
u64 result_cycle[2];
|
||||
|
||||
argc = parse_options(argc, argv, options,
|
||||
bench_mem_memset_usage, 0);
|
||||
|
||||
if (use_clock)
|
||||
init_clock();
|
||||
if (use_cycle)
|
||||
init_cycle();
|
||||
|
||||
len = (size_t)perf_atoll((char *)length_str);
|
||||
|
||||
result_clock[0] = result_clock[1] = 0ULL;
|
||||
result_cycle[0] = result_cycle[1] = 0ULL;
|
||||
result_bps[0] = result_bps[1] = 0.0;
|
||||
|
||||
if ((s64)len <= 0) {
|
||||
|
@ -217,11 +217,11 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
|
||||
if (!only_prefault && !no_prefault) {
|
||||
/* show both of results */
|
||||
if (use_clock) {
|
||||
result_clock[0] =
|
||||
do_memset_clock(routines[i].fn, len, false);
|
||||
result_clock[1] =
|
||||
do_memset_clock(routines[i].fn, len, true);
|
||||
if (use_cycle) {
|
||||
result_cycle[0] =
|
||||
do_memset_cycle(routines[i].fn, len, false);
|
||||
result_cycle[1] =
|
||||
do_memset_cycle(routines[i].fn, len, true);
|
||||
} else {
|
||||
result_bps[0] =
|
||||
do_memset_gettimeofday(routines[i].fn,
|
||||
|
@ -231,9 +231,9 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
len, true);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
result_clock[pf] =
|
||||
do_memset_clock(routines[i].fn,
|
||||
if (use_cycle) {
|
||||
result_cycle[pf] =
|
||||
do_memset_cycle(routines[i].fn,
|
||||
len, only_prefault);
|
||||
} else {
|
||||
result_bps[pf] =
|
||||
|
@ -245,12 +245,12 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte\n",
|
||||
(double)result_clock[0]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte\n",
|
||||
(double)result_cycle[0]
|
||||
/ (double)len);
|
||||
printf(" %14lf Clock/Byte (with prefault)\n ",
|
||||
(double)result_clock[1]
|
||||
printf(" %14lf Cycle/Byte (with prefault)\n ",
|
||||
(double)result_cycle[1]
|
||||
/ (double)len);
|
||||
} else {
|
||||
print_bps(result_bps[0]);
|
||||
|
@ -259,9 +259,9 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
printf(" (with prefault)\n");
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte",
|
||||
(double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte",
|
||||
(double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
print_bps(result_bps[pf]);
|
||||
|
@ -271,17 +271,17 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
break;
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
if (use_cycle) {
|
||||
printf("%lf %lf\n",
|
||||
(double)result_clock[0] / (double)len,
|
||||
(double)result_clock[1] / (double)len);
|
||||
(double)result_cycle[0] / (double)len,
|
||||
(double)result_cycle[1] / (double)len);
|
||||
} else {
|
||||
printf("%lf %lf\n",
|
||||
result_bps[0], result_bps[1]);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf("%lf\n", (double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf("%lf\n", (double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
printf("%lf\n", result_bps[pf]);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "cpumap.h"
|
||||
#include "thread_map.h"
|
||||
#include "target.h"
|
||||
#include "../../../include/linux/hw_breakpoint.h"
|
||||
|
||||
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
|
||||
#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
|
||||
|
@ -152,6 +153,31 @@ static int perf_evsel__sw_name(struct perf_evsel *evsel, char *bf, size_t size)
|
|||
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
|
||||
}
|
||||
|
||||
static int __perf_evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = scnprintf(bf, size, "mem:0x%" PRIx64 ":", addr);
|
||||
|
||||
if (type & HW_BREAKPOINT_R)
|
||||
r += scnprintf(bf + r, size - r, "r");
|
||||
|
||||
if (type & HW_BREAKPOINT_W)
|
||||
r += scnprintf(bf + r, size - r, "w");
|
||||
|
||||
if (type & HW_BREAKPOINT_X)
|
||||
r += scnprintf(bf + r, size - r, "x");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int perf_evsel__bp_name(struct perf_evsel *evsel, char *bf, size_t size)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int r = __perf_evsel__bp_name(bf, size, attr->bp_addr, attr->bp_type);
|
||||
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
|
||||
}
|
||||
|
||||
const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
|
||||
|
@ -285,6 +311,10 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
|
|||
scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint");
|
||||
break;
|
||||
|
||||
case PERF_TYPE_BREAKPOINT:
|
||||
perf_evsel__bp_name(evsel, bf, sizeof(bf));
|
||||
break;
|
||||
|
||||
default:
|
||||
scnprintf(bf, sizeof(bf), "%s", "unknown attr type");
|
||||
break;
|
||||
|
|
|
@ -108,4 +108,14 @@ int eprintf(int level,
|
|||
#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* This looks more complex than it should be. But we need to
|
||||
* get the type for the ~ right in round_down (it needs to be
|
||||
* as wide as the result!), and we want to evaluate the macro
|
||||
* arguments just once each.
|
||||
*/
|
||||
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
|
||||
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
|
||||
#define round_down(x, y) ((x) & ~__round_mask(x, y))
|
||||
|
||||
#endif
|
||||
|
|
|
@ -181,6 +181,22 @@ static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type",
|
||||
PERF_TYPE_BREAKPOINT == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong bp_type",
|
||||
(HW_BREAKPOINT_R|HW_BREAKPOINT_W) == evsel->attr.bp_type);
|
||||
TEST_ASSERT_VAL("wrong bp_len",
|
||||
HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
|
@ -309,6 +325,8 @@ static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
|
|||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong name",
|
||||
!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:u"));
|
||||
|
||||
return test__checkevent_breakpoint(evlist);
|
||||
}
|
||||
|
@ -322,6 +340,8 @@ static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
|
|||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong name",
|
||||
!strcmp(perf_evsel__name(evsel), "mem:0x0:x:k"));
|
||||
|
||||
return test__checkevent_breakpoint_x(evlist);
|
||||
}
|
||||
|
@ -335,6 +355,8 @@ static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
|
|||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong name",
|
||||
!strcmp(perf_evsel__name(evsel), "mem:0x0:r:hp"));
|
||||
|
||||
return test__checkevent_breakpoint_r(evlist);
|
||||
}
|
||||
|
@ -348,10 +370,27 @@ static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
|
|||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong name",
|
||||
!strcmp(perf_evsel__name(evsel), "mem:0x0:w:up"));
|
||||
|
||||
return test__checkevent_breakpoint_w(evlist);
|
||||
}
|
||||
|
||||
static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong name",
|
||||
!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:kp"));
|
||||
|
||||
return test__checkevent_breakpoint_rw(evlist);
|
||||
}
|
||||
|
||||
static int test__checkevent_pmu(struct perf_evlist *evlist)
|
||||
{
|
||||
|
||||
|
@ -585,10 +624,16 @@ static struct test__event_st test__events[] = {
|
|||
.name = "instructions:H",
|
||||
.check = test__checkevent_exclude_guest_modifier,
|
||||
},
|
||||
[26] = {
|
||||
.name = "mem:0:rw",
|
||||
.check = test__checkevent_breakpoint_rw,
|
||||
},
|
||||
[27] = {
|
||||
.name = "mem:0:rw:kp",
|
||||
.check = test__checkevent_breakpoint_rw_modifier,
|
||||
},
|
||||
};
|
||||
|
||||
#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
|
||||
|
||||
static struct test__event_st test__events_pmu[] = {
|
||||
[0] = {
|
||||
.name = "cpu/config=10,config1,config2=3,period=1000/u",
|
||||
|
@ -600,9 +645,6 @@ static struct test__event_st test__events_pmu[] = {
|
|||
},
|
||||
};
|
||||
|
||||
#define TEST__EVENTS_PMU_CNT (sizeof(test__events_pmu) / \
|
||||
sizeof(struct test__event_st))
|
||||
|
||||
struct test__term {
|
||||
const char *str;
|
||||
__u32 type;
|
||||
|
@ -718,21 +760,17 @@ int parse_events__test(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = test_events(test__events, TEST__EVENTS_CNT);
|
||||
if (ret)
|
||||
break;
|
||||
#define TEST_EVENTS(tests) \
|
||||
do { \
|
||||
ret = test_events(tests, ARRAY_SIZE(tests)); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
} while (0)
|
||||
|
||||
if (test_pmu()) {
|
||||
ret = test_events(test__events_pmu,
|
||||
TEST__EVENTS_PMU_CNT);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
TEST_EVENTS(test__events);
|
||||
|
||||
ret = test_terms(test__terms, TEST__TERMS_CNT);
|
||||
if (test_pmu())
|
||||
TEST_EVENTS(test__events_pmu);
|
||||
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
return test_terms(test__terms, ARRAY_SIZE(test__terms));
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#define MAX_NAME_LEN 100
|
||||
|
||||
struct event_symbol {
|
||||
u8 type;
|
||||
u64 config;
|
||||
const char *symbol;
|
||||
const char *alias;
|
||||
};
|
||||
|
@ -30,30 +28,86 @@ extern int parse_events_debug;
|
|||
#endif
|
||||
int parse_events_parse(void *data, void *scanner);
|
||||
|
||||
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
|
||||
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
|
||||
static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = {
|
||||
.symbol = "cpu-cycles",
|
||||
.alias = "cycles",
|
||||
},
|
||||
[PERF_COUNT_HW_INSTRUCTIONS] = {
|
||||
.symbol = "instructions",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_HW_CACHE_REFERENCES] = {
|
||||
.symbol = "cache-references",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_HW_CACHE_MISSES] = {
|
||||
.symbol = "cache-misses",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = {
|
||||
.symbol = "branch-instructions",
|
||||
.alias = "branches",
|
||||
},
|
||||
[PERF_COUNT_HW_BRANCH_MISSES] = {
|
||||
.symbol = "branch-misses",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_HW_BUS_CYCLES] = {
|
||||
.symbol = "bus-cycles",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = {
|
||||
.symbol = "stalled-cycles-frontend",
|
||||
.alias = "idle-cycles-frontend",
|
||||
},
|
||||
[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = {
|
||||
.symbol = "stalled-cycles-backend",
|
||||
.alias = "idle-cycles-backend",
|
||||
},
|
||||
[PERF_COUNT_HW_REF_CPU_CYCLES] = {
|
||||
.symbol = "ref-cycles",
|
||||
.alias = "",
|
||||
},
|
||||
};
|
||||
|
||||
static struct event_symbol event_symbols[] = {
|
||||
{ CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
|
||||
{ CHW(STALLED_CYCLES_FRONTEND), "stalled-cycles-frontend", "idle-cycles-frontend" },
|
||||
{ CHW(STALLED_CYCLES_BACKEND), "stalled-cycles-backend", "idle-cycles-backend" },
|
||||
{ CHW(INSTRUCTIONS), "instructions", "" },
|
||||
{ CHW(CACHE_REFERENCES), "cache-references", "" },
|
||||
{ CHW(CACHE_MISSES), "cache-misses", "" },
|
||||
{ CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
|
||||
{ CHW(BRANCH_MISSES), "branch-misses", "" },
|
||||
{ CHW(BUS_CYCLES), "bus-cycles", "" },
|
||||
{ CHW(REF_CPU_CYCLES), "ref-cycles", "" },
|
||||
|
||||
{ CSW(CPU_CLOCK), "cpu-clock", "" },
|
||||
{ CSW(TASK_CLOCK), "task-clock", "" },
|
||||
{ CSW(PAGE_FAULTS), "page-faults", "faults" },
|
||||
{ CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
|
||||
{ CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
|
||||
{ CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
|
||||
{ CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
|
||||
{ CSW(ALIGNMENT_FAULTS), "alignment-faults", "" },
|
||||
{ CSW(EMULATION_FAULTS), "emulation-faults", "" },
|
||||
static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
|
||||
[PERF_COUNT_SW_CPU_CLOCK] = {
|
||||
.symbol = "cpu-clock",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_TASK_CLOCK] = {
|
||||
.symbol = "task-clock",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_PAGE_FAULTS] = {
|
||||
.symbol = "page-faults",
|
||||
.alias = "faults",
|
||||
},
|
||||
[PERF_COUNT_SW_CONTEXT_SWITCHES] = {
|
||||
.symbol = "context-switches",
|
||||
.alias = "cs",
|
||||
},
|
||||
[PERF_COUNT_SW_CPU_MIGRATIONS] = {
|
||||
.symbol = "cpu-migrations",
|
||||
.alias = "migrations",
|
||||
},
|
||||
[PERF_COUNT_SW_PAGE_FAULTS_MIN] = {
|
||||
.symbol = "minor-faults",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_PAGE_FAULTS_MAJ] = {
|
||||
.symbol = "major-faults",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_ALIGNMENT_FAULTS] = {
|
||||
.symbol = "alignment-faults",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_EMULATION_FAULTS] = {
|
||||
.symbol = "emulation-faults",
|
||||
.alias = "",
|
||||
},
|
||||
};
|
||||
|
||||
#define __PERF_EVENT_FIELD(config, name) \
|
||||
|
@ -383,21 +437,31 @@ parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
|
|||
if (!type || !type[i])
|
||||
break;
|
||||
|
||||
#define CHECK_SET_TYPE(bit) \
|
||||
do { \
|
||||
if (attr->bp_type & bit) \
|
||||
return -EINVAL; \
|
||||
else \
|
||||
attr->bp_type |= bit; \
|
||||
} while (0)
|
||||
|
||||
switch (type[i]) {
|
||||
case 'r':
|
||||
attr->bp_type |= HW_BREAKPOINT_R;
|
||||
CHECK_SET_TYPE(HW_BREAKPOINT_R);
|
||||
break;
|
||||
case 'w':
|
||||
attr->bp_type |= HW_BREAKPOINT_W;
|
||||
CHECK_SET_TYPE(HW_BREAKPOINT_W);
|
||||
break;
|
||||
case 'x':
|
||||
attr->bp_type |= HW_BREAKPOINT_X;
|
||||
CHECK_SET_TYPE(HW_BREAKPOINT_X);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#undef CHECK_SET_TYPE
|
||||
|
||||
if (!attr->bp_type) /* Default */
|
||||
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
|
||||
|
||||
|
@ -408,7 +472,6 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
|
|||
void *ptr, char *type)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
char name[MAX_NAME_LEN];
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.bp_addr = (unsigned long) ptr;
|
||||
|
@ -427,8 +490,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
|
|||
|
||||
attr.type = PERF_TYPE_BREAKPOINT;
|
||||
|
||||
snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
|
||||
return add_event(list, idx, &attr, name);
|
||||
return add_event(list, idx, &attr, NULL);
|
||||
}
|
||||
|
||||
static int config_term(struct perf_event_attr *attr,
|
||||
|
@ -816,16 +878,13 @@ int is_valid_tracepoint(const char *event_string)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void print_events_type(u8 type)
|
||||
static void __print_events_type(u8 type, struct event_symbol *syms,
|
||||
unsigned max)
|
||||
{
|
||||
struct event_symbol *syms = event_symbols;
|
||||
unsigned int i;
|
||||
char name[64];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
|
||||
if (type != syms->type)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < max ; i++, syms++) {
|
||||
if (strlen(syms->alias))
|
||||
snprintf(name, sizeof(name), "%s OR %s",
|
||||
syms->symbol, syms->alias);
|
||||
|
@ -837,6 +896,14 @@ void print_events_type(u8 type)
|
|||
}
|
||||
}
|
||||
|
||||
void print_events_type(u8 type)
|
||||
{
|
||||
if (type == PERF_TYPE_SOFTWARE)
|
||||
__print_events_type(type, event_symbols_sw, PERF_COUNT_SW_MAX);
|
||||
else
|
||||
__print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX);
|
||||
}
|
||||
|
||||
int print_hwcache_events(const char *event_glob)
|
||||
{
|
||||
unsigned int type, op, i, printed = 0;
|
||||
|
@ -864,26 +931,13 @@ int print_hwcache_events(const char *event_glob)
|
|||
return printed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the help text for the event symbols:
|
||||
*/
|
||||
void print_events(const char *event_glob)
|
||||
static void print_symbol_events(const char *event_glob, unsigned type,
|
||||
struct event_symbol *syms, unsigned max)
|
||||
{
|
||||
unsigned int i, type, prev_type = -1, printed = 0, ntypes_printed = 0;
|
||||
struct event_symbol *syms = event_symbols;
|
||||
unsigned i, printed = 0;
|
||||
char name[MAX_NAME_LEN];
|
||||
|
||||
printf("\n");
|
||||
printf("List of pre-defined events (to be used in -e):\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
|
||||
type = syms->type;
|
||||
|
||||
if (type != prev_type && printed) {
|
||||
printf("\n");
|
||||
printed = 0;
|
||||
ntypes_printed++;
|
||||
}
|
||||
for (i = 0; i < max; i++, syms++) {
|
||||
|
||||
if (event_glob != NULL &&
|
||||
!(strglobmatch(syms->symbol, event_glob) ||
|
||||
|
@ -894,17 +948,31 @@ void print_events(const char *event_glob)
|
|||
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
|
||||
else
|
||||
strncpy(name, syms->symbol, MAX_NAME_LEN);
|
||||
printf(" %-50s [%s]\n", name,
|
||||
event_type_descriptors[type]);
|
||||
|
||||
prev_type = type;
|
||||
++printed;
|
||||
printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
|
||||
|
||||
printed++;
|
||||
}
|
||||
|
||||
if (ntypes_printed) {
|
||||
printed = 0;
|
||||
if (printed)
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the help text for the event symbols:
|
||||
*/
|
||||
void print_events(const char *event_glob)
|
||||
{
|
||||
|
||||
printf("\n");
|
||||
printf("List of pre-defined events (to be used in -e):\n");
|
||||
|
||||
print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
|
||||
event_symbols_hw, PERF_COUNT_HW_MAX);
|
||||
|
||||
print_symbol_events(event_glob, PERF_TYPE_SOFTWARE,
|
||||
event_symbols_sw, PERF_COUNT_SW_MAX);
|
||||
|
||||
print_hwcache_events(event_glob);
|
||||
|
||||
if (event_glob != NULL)
|
||||
|
|
|
@ -56,7 +56,7 @@ static int sym(yyscan_t scanner, int type, int config)
|
|||
YYSTYPE *yylval = parse_events_get_lval(scanner);
|
||||
|
||||
yylval->num = (type << 16) + config;
|
||||
return PE_VALUE_SYM;
|
||||
return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW;
|
||||
}
|
||||
|
||||
static int term(yyscan_t scanner, int type)
|
||||
|
@ -76,7 +76,7 @@ num_hex 0x[a-fA-F0-9]+
|
|||
num_raw_hex [a-fA-F0-9]+
|
||||
name [a-zA-Z_*?][a-zA-Z0-9_*?]*
|
||||
modifier_event [ukhpGH]{1,8}
|
||||
modifier_bp [rwx]
|
||||
modifier_bp [rwx]{1,3}
|
||||
|
||||
%%
|
||||
|
||||
|
@ -152,6 +152,7 @@ r{num_raw_hex} { return raw(yyscanner); }
|
|||
, { return ','; }
|
||||
: { return ':'; }
|
||||
= { return '='; }
|
||||
\n { }
|
||||
|
||||
<mem>{
|
||||
{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); }
|
||||
|
|
|
@ -26,14 +26,15 @@ do { \
|
|||
%}
|
||||
|
||||
%token PE_START_EVENTS PE_START_TERMS
|
||||
%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM
|
||||
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
|
||||
%token PE_NAME
|
||||
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
|
||||
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
|
||||
%token PE_PREFIX_MEM PE_PREFIX_RAW
|
||||
%token PE_ERROR
|
||||
%type <num> PE_VALUE
|
||||
%type <num> PE_VALUE_SYM
|
||||
%type <num> PE_VALUE_SYM_HW
|
||||
%type <num> PE_VALUE_SYM_SW
|
||||
%type <num> PE_RAW
|
||||
%type <num> PE_TERM
|
||||
%type <str> PE_NAME
|
||||
|
@ -41,6 +42,7 @@ do { \
|
|||
%type <str> PE_NAME_CACHE_OP_RESULT
|
||||
%type <str> PE_MODIFIER_EVENT
|
||||
%type <str> PE_MODIFIER_BP
|
||||
%type <num> value_sym
|
||||
%type <head> event_config
|
||||
%type <term> event_term
|
||||
%type <head> event_pmu
|
||||
|
@ -109,8 +111,13 @@ PE_NAME '/' event_config '/'
|
|||
$$ = list;
|
||||
}
|
||||
|
||||
value_sym:
|
||||
PE_VALUE_SYM_HW
|
||||
|
|
||||
PE_VALUE_SYM_SW
|
||||
|
||||
event_legacy_symbol:
|
||||
PE_VALUE_SYM '/' event_config '/'
|
||||
value_sym '/' event_config '/'
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
|
@ -123,7 +130,7 @@ PE_VALUE_SYM '/' event_config '/'
|
|||
$$ = list;
|
||||
}
|
||||
|
|
||||
PE_VALUE_SYM sep_slash_dc
|
||||
value_sym sep_slash_dc
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
|
|
|
@ -72,7 +72,7 @@ static int pmu_format(char *name, struct list_head *format)
|
|||
"%s/bus/event_source/devices/%s/format", sysfs, name);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return -1;
|
||||
return 0; /* no error if format does not exist */
|
||||
|
||||
if (pmu_format_parse(path, format))
|
||||
return -1;
|
||||
|
@ -252,6 +252,7 @@ static struct perf_pmu *pmu_lookup(char *name)
|
|||
list_splice(&aliases, &pmu->aliases);
|
||||
pmu->name = strdup(name);
|
||||
pmu->type = type;
|
||||
list_add_tail(&pmu->list, &pmus);
|
||||
return pmu;
|
||||
}
|
||||
|
||||
|
|
|
@ -209,6 +209,10 @@ static void define_event_symbols(struct event_format *event,
|
|||
define_symbolic_values(args->symbol.symbols, ev_name,
|
||||
cur_field_name);
|
||||
break;
|
||||
case PRINT_HEX:
|
||||
define_event_symbols(event, ev_name, args->hex.field);
|
||||
define_event_symbols(event, ev_name, args->hex.size);
|
||||
break;
|
||||
case PRINT_BSTRING:
|
||||
case PRINT_DYNAMIC_ARRAY:
|
||||
case PRINT_STRING:
|
||||
|
|
|
@ -166,6 +166,10 @@ static void define_event_symbols(struct event_format *event,
|
|||
define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name,
|
||||
cur_field_name);
|
||||
break;
|
||||
case PRINT_HEX:
|
||||
define_event_symbols(event, ev_name, args->hex.field);
|
||||
define_event_symbols(event, ev_name, args->hex.size);
|
||||
break;
|
||||
case PRINT_STRING:
|
||||
break;
|
||||
case PRINT_TYPE:
|
||||
|
|
|
@ -1478,14 +1478,31 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size)
|
|||
goto out;
|
||||
}
|
||||
|
||||
sec = elf_section_by_name(elf, &ehdr, &shdr,
|
||||
".note.gnu.build-id", NULL);
|
||||
if (sec == NULL) {
|
||||
/*
|
||||
* Check following sections for notes:
|
||||
* '.note.gnu.build-id'
|
||||
* '.notes'
|
||||
* '.note' (VDSO specific)
|
||||
*/
|
||||
do {
|
||||
sec = elf_section_by_name(elf, &ehdr, &shdr,
|
||||
".note.gnu.build-id", NULL);
|
||||
if (sec)
|
||||
break;
|
||||
|
||||
sec = elf_section_by_name(elf, &ehdr, &shdr,
|
||||
".notes", NULL);
|
||||
if (sec == NULL)
|
||||
goto out;
|
||||
}
|
||||
if (sec)
|
||||
break;
|
||||
|
||||
sec = elf_section_by_name(elf, &ehdr, &shdr,
|
||||
".note", NULL);
|
||||
if (sec)
|
||||
break;
|
||||
|
||||
return err;
|
||||
|
||||
} while (0);
|
||||
|
||||
data = elf_getdata(sec, NULL);
|
||||
if (data == NULL)
|
||||
|
|
Загрузка…
Ссылка в новой задаче