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:
Ingo Molnar 2012-07-06 10:17:40 +02:00
commit 387ef4e24a
17 changed files with 733 additions and 321 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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::

View file

@ -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]);

View file

@ -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]);

View file

@ -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;

View file

@ -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

View file

@ -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));
}

View file

@ -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)

View file

@ -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); }

View file

@ -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;

View file

@ -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;
}

View file

@ -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:

View file

@ -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:

View file

@ -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)