mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-15 14:10:43 +00:00
perf/core improvements and fixes:
. Add new COMM infrastructure, further improving histogram processing, from Frédéric Weisbecker, one fix from Namhyung Kim. . Enhance option parse error message, showing just the help lines of the options affected, from Namhyung Kim. . Fixup PERF_SAMPLE_TRANSACTION handling in sample synthesizing and 'perf test', from Adrian Hunter. . Set up output options for in-stream attributes, from Adrian Hunter. . Fix 32-bit cross build, from Adrian Hunter. . Fix libunwind build and feature detection for 32-bit build, from Adrian Hunter. . Always use perf_evsel__set_sample_bit to set sample_type, from Adrian Hunter. perf evlist: Add a debug print if event buffer mmap fails . Add missing data.h into LIB_H headers, fix from Jiri Olsa. . libtraceevent updates from upstream trace-cmd repo, from Steven Rostedt. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJSd96kAAoJENZQFvNTUqpAHYYQAKzfOyOJsmcS03iFTaEXOP3X l4q5fefWfVKFfCiEvBqE1TsFNCrf8Vmi1lzBDeUid92CJ0/ItURmrkZMPQ78VJjo Pj6Esii8pKlJs1W3w9T68UzWK7YJXtqyp2DWgNTGgUP3lMBvd1uoD+PxYpAqDued xiNQz3s1CURVQqCStKpup4vOyX1bLr//zr8cOb5AoLkpiDJa1waPyDSK1q6AaQaA a7JclGAIClImkU7cKHMyId3iS+LivS90qKzDTVemNuEjvTJ/php4iS5Yc/wMvvaw 4WGOxuYxZrM59QJ5/xH9ULztRZkCSgClMe63/NfntNii1YAqD6rlhzZvyp7D5CCF kElEGy/K6jpLos4Ox37FdoPg/kNw1EL96P5sFkB8vTT8nHe3PwPWe0HeyXQp0pnK 7wPtiv66Q3+u9EDMjEDSoyaDfeqIzg2wIeU7ClUr0CHXm+SUFuRdXl+epmz/vyrf /3Cupujh9IedpJEWSdUAhpxKcS31i3XeB+oMuhMJRNxKm1fkAwHf7bJ4h9uWBXP/ DWMrd0c+AEShCETkdEl9Ape2vLOcH8kG70VFem18OwjHJ+Y9boHnvsCdZfCpMqzn 0lYQ9jAR0OrIpdZMWsw1UNdawmtgH+KnPzd3A3SxDO0Q3NDROMIFEnQbaG47FFIN 2gMREA/Lk43p6yRZXLgV =gbfw -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: * Add new COMM infrastructure, further improving histogram processing, from Frédéric Weisbecker, one fix from Namhyung Kim. * Enhance option parse error message, showing just the help lines of the options affected, from Namhyung Kim. * Fixup PERF_SAMPLE_TRANSACTION handling in sample synthesizing and 'perf test', from Adrian Hunter. * Set up output options for in-stream attributes, from Adrian Hunter. * Fix 32-bit cross build, from Adrian Hunter. * Fix libunwind build and feature detection for 32-bit build, from Adrian Hunter. * Always use perf_evsel__set_sample_bit to set sample_type, from Adrian Hunter. perf evlist: Add a debug print if event buffer mmap fails * Add missing data.h into LIB_H headers, fix from Jiri Olsa. * libtraceevent updates from upstream trace-cmd repo, from Steven Rostedt. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
87968f94fb
@ -305,6 +305,11 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock)
|
||||
{
|
||||
pevent->trace_clock = trace_clock;
|
||||
}
|
||||
|
||||
struct func_map {
|
||||
unsigned long long addr;
|
||||
char *func;
|
||||
@ -599,10 +604,11 @@ find_printk(struct pevent *pevent, unsigned long long addr)
|
||||
* This registers a string by the address it was stored in the kernel.
|
||||
* The @fmt passed in is duplicated.
|
||||
*/
|
||||
int pevent_register_print_string(struct pevent *pevent, char *fmt,
|
||||
int pevent_register_print_string(struct pevent *pevent, const char *fmt,
|
||||
unsigned long long addr)
|
||||
{
|
||||
struct printk_list *item = malloc(sizeof(*item));
|
||||
char *p;
|
||||
|
||||
if (!item)
|
||||
return -1;
|
||||
@ -610,10 +616,21 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt,
|
||||
item->next = pevent->printklist;
|
||||
item->addr = addr;
|
||||
|
||||
/* Strip off quotes and '\n' from the end */
|
||||
if (fmt[0] == '"')
|
||||
fmt++;
|
||||
item->printk = strdup(fmt);
|
||||
if (!item->printk)
|
||||
goto out_free;
|
||||
|
||||
p = item->printk + strlen(item->printk) - 1;
|
||||
if (*p == '"')
|
||||
*p = 0;
|
||||
|
||||
p -= 2;
|
||||
if (strcmp(p, "\\n") == 0)
|
||||
*p = 0;
|
||||
|
||||
pevent->printklist = item;
|
||||
pevent->printk_count++;
|
||||
|
||||
@ -3488,6 +3505,7 @@ 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;
|
||||
struct printk_map *printk;
|
||||
unsigned long long val, fval;
|
||||
unsigned long addr;
|
||||
char *str;
|
||||
@ -3523,7 +3541,12 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
||||
if (!(field->flags & FIELD_IS_ARRAY) &&
|
||||
field->size == pevent->long_size) {
|
||||
addr = *(unsigned long *)(data + field->offset);
|
||||
trace_seq_printf(s, "%lx", addr);
|
||||
/* Check if it matches a print format */
|
||||
printk = find_printk(pevent, addr);
|
||||
if (printk)
|
||||
trace_seq_puts(s, printk->printk);
|
||||
else
|
||||
trace_seq_printf(s, "%lx", addr);
|
||||
break;
|
||||
}
|
||||
str = malloc(len + 1);
|
||||
@ -3565,15 +3588,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)
|
||||
goto out_warning_field;
|
||||
arg->hex.field->field.field = field;
|
||||
if (arg->hex.field->type == PRINT_DYNAMIC_ARRAY) {
|
||||
unsigned long offset;
|
||||
offset = pevent_read_number(pevent,
|
||||
data + arg->hex.field->dynarray.field->offset,
|
||||
arg->hex.field->dynarray.field->size);
|
||||
hex = data + (offset & 0xffff);
|
||||
} else {
|
||||
field = arg->hex.field->field.field;
|
||||
if (!field) {
|
||||
str = arg->hex.field->field.name;
|
||||
field = pevent_find_any_field(event, str);
|
||||
if (!field)
|
||||
goto out_warning_field;
|
||||
arg->hex.field->field.field = field;
|
||||
}
|
||||
hex = data + field->offset;
|
||||
}
|
||||
hex = data + field->offset;
|
||||
len = eval_num_arg(data, size, event, arg->hex.size);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i)
|
||||
@ -3771,8 +3802,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
||||
if (asprintf(&arg->atom.atom, "%lld", ip) < 0)
|
||||
goto out_free;
|
||||
|
||||
/* skip the first "%pf : " */
|
||||
for (ptr = fmt + 6, bptr = data + field->offset;
|
||||
/* skip the first "%pf: " */
|
||||
for (ptr = fmt + 5, bptr = data + field->offset;
|
||||
bptr < data + size && *ptr; ptr++) {
|
||||
int ls = 0;
|
||||
|
||||
@ -3882,7 +3913,6 @@ get_bprint_format(void *data, int size __maybe_unused,
|
||||
struct format_field *field;
|
||||
struct printk_map *printk;
|
||||
char *format;
|
||||
char *p;
|
||||
|
||||
field = pevent->bprint_fmt_field;
|
||||
|
||||
@ -3899,25 +3929,13 @@ get_bprint_format(void *data, int size __maybe_unused,
|
||||
|
||||
printk = find_printk(pevent, addr);
|
||||
if (!printk) {
|
||||
if (asprintf(&format, "%%pf : (NO FORMAT FOUND at %llx)\n", addr) < 0)
|
||||
if (asprintf(&format, "%%pf: (NO FORMAT FOUND at %llx)\n", addr) < 0)
|
||||
return NULL;
|
||||
return format;
|
||||
}
|
||||
|
||||
p = printk->printk;
|
||||
/* Remove any quotes. */
|
||||
if (*p == '"')
|
||||
p++;
|
||||
if (asprintf(&format, "%s : %s", "%pf", p) < 0)
|
||||
if (asprintf(&format, "%s: %s", "%pf", printk->printk) < 0)
|
||||
return NULL;
|
||||
/* remove ending quotes and new line since we will add one too */
|
||||
p = format + strlen(format) - 1;
|
||||
if (*p == '"')
|
||||
*p = 0;
|
||||
|
||||
p -= 2;
|
||||
if (strcmp(p, "\\n") == 0)
|
||||
*p = 0;
|
||||
|
||||
return format;
|
||||
}
|
||||
@ -3963,7 +3981,7 @@ 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]))
|
||||
if (!isprint(p[i]) && !isspace(p[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@ -4428,11 +4446,11 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event,
|
||||
{
|
||||
int print_pretty = 1;
|
||||
|
||||
if (event->pevent->print_raw)
|
||||
if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW))
|
||||
print_event_fields(s, record->data, record->size, event);
|
||||
else {
|
||||
|
||||
if (event->handler)
|
||||
if (event->handler && !(event->flags & EVENT_FL_NOHANDLE))
|
||||
print_pretty = event->handler(s, record, event,
|
||||
event->context);
|
||||
|
||||
@ -4443,8 +4461,21 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event,
|
||||
trace_seq_terminate(s);
|
||||
}
|
||||
|
||||
static bool is_timestamp_in_us(char *trace_clock, bool use_trace_clock)
|
||||
{
|
||||
if (!use_trace_clock)
|
||||
return true;
|
||||
|
||||
if (!strcmp(trace_clock, "local") || !strcmp(trace_clock, "global")
|
||||
|| !strcmp(trace_clock, "uptime") || !strcmp(trace_clock, "perf"))
|
||||
return true;
|
||||
|
||||
/* trace_clock is setting in tsc or counter mode */
|
||||
return false;
|
||||
}
|
||||
|
||||
void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
|
||||
struct pevent_record *record)
|
||||
struct pevent_record *record, bool use_trace_clock)
|
||||
{
|
||||
static const char *spaces = " "; /* 20 spaces */
|
||||
struct event_format *event;
|
||||
@ -4457,9 +4488,14 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
|
||||
int pid;
|
||||
int len;
|
||||
int p;
|
||||
bool use_usec_format;
|
||||
|
||||
secs = record->ts / NSECS_PER_SEC;
|
||||
nsecs = record->ts - secs * NSECS_PER_SEC;
|
||||
use_usec_format = is_timestamp_in_us(pevent->trace_clock,
|
||||
use_trace_clock);
|
||||
if (use_usec_format) {
|
||||
secs = record->ts / NSECS_PER_SEC;
|
||||
nsecs = record->ts - secs * NSECS_PER_SEC;
|
||||
}
|
||||
|
||||
if (record->size < 0) {
|
||||
do_warning("ug! negative record size %d", record->size);
|
||||
@ -4484,15 +4520,20 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
|
||||
} else
|
||||
trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu);
|
||||
|
||||
if (pevent->flags & PEVENT_NSEC_OUTPUT) {
|
||||
usecs = nsecs;
|
||||
p = 9;
|
||||
} else {
|
||||
usecs = (nsecs + 500) / NSECS_PER_USEC;
|
||||
p = 6;
|
||||
}
|
||||
if (use_usec_format) {
|
||||
if (pevent->flags & PEVENT_NSEC_OUTPUT) {
|
||||
usecs = nsecs;
|
||||
p = 9;
|
||||
} else {
|
||||
usecs = (nsecs + 500) / NSECS_PER_USEC;
|
||||
p = 6;
|
||||
}
|
||||
|
||||
trace_seq_printf(s, " %5lu.%0*lu: %s: ", secs, p, usecs, event->name);
|
||||
trace_seq_printf(s, " %5lu.%0*lu: %s: ",
|
||||
secs, p, usecs, event->name);
|
||||
} else
|
||||
trace_seq_printf(s, " %12llu: %s: ",
|
||||
record->ts, event->name);
|
||||
|
||||
/* Space out the event names evenly. */
|
||||
len = strlen(event->name);
|
||||
@ -5326,6 +5367,48 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pevent_print_func_field - print a field and a format for function pointers
|
||||
* @s: The seq to print to
|
||||
* @fmt: The printf format to print the field with.
|
||||
* @event: the event that the field is for
|
||||
* @name: The name of the field
|
||||
* @record: The record with the field name.
|
||||
* @err: print default error if failed.
|
||||
*
|
||||
* Returns: 0 on success, -1 field not found, or 1 if buffer is full.
|
||||
*/
|
||||
int pevent_print_func_field(struct trace_seq *s, const char *fmt,
|
||||
struct event_format *event, const char *name,
|
||||
struct pevent_record *record, int err)
|
||||
{
|
||||
struct format_field *field = pevent_find_field(event, name);
|
||||
struct pevent *pevent = event->pevent;
|
||||
unsigned long long val;
|
||||
struct func_map *func;
|
||||
char tmp[128];
|
||||
|
||||
if (!field)
|
||||
goto failed;
|
||||
|
||||
if (pevent_read_number_field(field, record->data, &val))
|
||||
goto failed;
|
||||
|
||||
func = find_func(pevent, val);
|
||||
|
||||
if (func)
|
||||
snprintf(tmp, 128, "%s/0x%llx", func->func, func->addr - val);
|
||||
else
|
||||
sprintf(tmp, "0x%08llx", val);
|
||||
|
||||
return trace_seq_printf(s, fmt, tmp);
|
||||
|
||||
failed:
|
||||
if (err)
|
||||
trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void free_func_handle(struct pevent_function_handler *func)
|
||||
{
|
||||
struct pevent_func_params *params;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#ifndef _PARSE_EVENTS_H
|
||||
#define _PARSE_EVENTS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <regex.h>
|
||||
|
||||
@ -307,6 +308,8 @@ enum {
|
||||
EVENT_FL_ISBPRINT = 0x04,
|
||||
EVENT_FL_ISFUNCENT = 0x10,
|
||||
EVENT_FL_ISFUNCRET = 0x20,
|
||||
EVENT_FL_NOHANDLE = 0x40,
|
||||
EVENT_FL_PRINTRAW = 0x80,
|
||||
|
||||
EVENT_FL_FAILED = 0x80000000
|
||||
};
|
||||
@ -450,6 +453,8 @@ struct pevent {
|
||||
|
||||
/* cache */
|
||||
struct event_format *last_event;
|
||||
|
||||
char *trace_clock;
|
||||
};
|
||||
|
||||
static inline void pevent_set_flag(struct pevent *pevent, int flag)
|
||||
@ -527,14 +532,15 @@ enum trace_flag_type {
|
||||
};
|
||||
|
||||
int pevent_register_comm(struct pevent *pevent, const char *comm, int pid);
|
||||
void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock);
|
||||
int pevent_register_function(struct pevent *pevent, char *name,
|
||||
unsigned long long addr, char *mod);
|
||||
int pevent_register_print_string(struct pevent *pevent, char *fmt,
|
||||
int pevent_register_print_string(struct pevent *pevent, const char *fmt,
|
||||
unsigned long long addr);
|
||||
int pevent_pid_is_registered(struct pevent *pevent, int pid);
|
||||
|
||||
void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
|
||||
struct pevent_record *record);
|
||||
struct pevent_record *record, bool use_trace_clock);
|
||||
|
||||
int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size,
|
||||
int long_size);
|
||||
@ -563,6 +569,10 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt,
|
||||
struct event_format *event, const char *name,
|
||||
struct pevent_record *record, int err);
|
||||
|
||||
int pevent_print_func_field(struct trace_seq *s, const char *fmt,
|
||||
struct event_format *event, const char *name,
|
||||
struct pevent_record *record, int err);
|
||||
|
||||
int pevent_register_event_handler(struct pevent *pevent, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context);
|
||||
|
@ -273,6 +273,7 @@ LIB_H += util/color.h
|
||||
LIB_H += util/values.h
|
||||
LIB_H += util/sort.h
|
||||
LIB_H += util/hist.h
|
||||
LIB_H += util/comm.h
|
||||
LIB_H += util/thread.h
|
||||
LIB_H += util/thread_map.h
|
||||
LIB_H += util/trace-event.h
|
||||
@ -295,6 +296,7 @@ LIB_H += ui/helpline.h
|
||||
LIB_H += ui/progress.h
|
||||
LIB_H += ui/util.h
|
||||
LIB_H += ui/ui.h
|
||||
LIB_H += util/data.h
|
||||
|
||||
LIB_OBJS += $(OUTPUT)util/abspath.o
|
||||
LIB_OBJS += $(OUTPUT)util/alias.o
|
||||
@ -340,6 +342,7 @@ LIB_OBJS += $(OUTPUT)util/machine.o
|
||||
LIB_OBJS += $(OUTPUT)util/map.o
|
||||
LIB_OBJS += $(OUTPUT)util/pstack.o
|
||||
LIB_OBJS += $(OUTPUT)util/session.o
|
||||
LIB_OBJS += $(OUTPUT)util/comm.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread_map.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
|
||||
@ -708,7 +711,7 @@ $(LIB_FILE): $(LIB_OBJS)
|
||||
TE_SOURCES = $(wildcard $(TRACE_EVENT_DIR)*.[ch])
|
||||
|
||||
$(LIBTRACEEVENT): $(TE_SOURCES)
|
||||
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a
|
||||
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) CFLAGS="-g -Wall $(EXTRA_CFLAGS)" libtraceevent.a
|
||||
|
||||
$(LIBTRACEEVENT)-clean:
|
||||
$(call QUIET_CLEAN, libtraceevent)
|
||||
|
@ -315,7 +315,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
return -1;
|
||||
}
|
||||
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid);
|
||||
dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
|
||||
|
||||
if (evsel->handler.func != NULL) {
|
||||
tracepoint_handler f = evsel->handler.func;
|
||||
|
@ -767,7 +767,7 @@ static void dump_threads(void)
|
||||
while (node) {
|
||||
st = container_of(node, struct thread_stat, rb);
|
||||
t = perf_session__findnew(session, st->tid);
|
||||
pr_info("%10d: %s\n", st->tid, t->comm);
|
||||
pr_info("%10d: %s\n", st->tid, thread__comm_str(t));
|
||||
node = rb_next(node);
|
||||
};
|
||||
}
|
||||
|
@ -905,13 +905,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
input_name = "perf.data";
|
||||
}
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
file.path = input_name;
|
||||
file.force = report.force;
|
||||
|
||||
@ -954,8 +947,22 @@ repeat:
|
||||
sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
|
||||
}
|
||||
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(report_usage, options);
|
||||
if (setup_sorting() < 0) {
|
||||
parse_options_usage(report_usage, options, "s", 1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (parent_pattern != default_parent_pattern) {
|
||||
if (sort_dimension__add("parent") < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
/*
|
||||
* Only in the TUI browser we are doing integrated annotation,
|
||||
@ -986,11 +993,6 @@ repeat:
|
||||
if (symbol__init() < 0)
|
||||
goto error;
|
||||
|
||||
if (parent_pattern != default_parent_pattern) {
|
||||
if (sort_dimension__add("parent") < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
/*
|
||||
* Special case: if there's an argument left then assume that
|
||||
|
@ -737,12 +737,12 @@ static int replay_fork_event(struct perf_sched *sched,
|
||||
|
||||
if (verbose) {
|
||||
printf("fork event\n");
|
||||
printf("... parent: %s/%d\n", parent->comm, parent->tid);
|
||||
printf("... child: %s/%d\n", child->comm, child->tid);
|
||||
printf("... parent: %s/%d\n", thread__comm_str(parent), parent->tid);
|
||||
printf("... child: %s/%d\n", thread__comm_str(child), child->tid);
|
||||
}
|
||||
|
||||
register_pid(sched, parent->tid, parent->comm);
|
||||
register_pid(sched, child->tid, child->comm);
|
||||
register_pid(sched, parent->tid, thread__comm_str(parent));
|
||||
register_pid(sched, child->tid, thread__comm_str(child));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1077,7 +1077,7 @@ static int latency_migrate_task_event(struct perf_sched *sched,
|
||||
if (!atoms) {
|
||||
if (thread_atoms_insert(sched, migrant))
|
||||
return -1;
|
||||
register_pid(sched, migrant->tid, migrant->comm);
|
||||
register_pid(sched, migrant->tid, thread__comm_str(migrant));
|
||||
atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
|
||||
if (!atoms) {
|
||||
pr_err("migration-event: Internal tree error");
|
||||
@ -1111,13 +1111,13 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_
|
||||
/*
|
||||
* Ignore idle threads:
|
||||
*/
|
||||
if (!strcmp(work_list->thread->comm, "swapper"))
|
||||
if (!strcmp(thread__comm_str(work_list->thread), "swapper"))
|
||||
return;
|
||||
|
||||
sched->all_runtime += work_list->total_runtime;
|
||||
sched->all_count += work_list->nb_atoms;
|
||||
|
||||
ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->tid);
|
||||
ret = printf(" %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid);
|
||||
|
||||
for (i = 0; i < 24 - ret; i++)
|
||||
printf(" ");
|
||||
@ -1334,7 +1334,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
printf(" %12.6f secs ", (double)timestamp/1e9);
|
||||
if (new_shortname) {
|
||||
printf("%s => %s:%d\n",
|
||||
sched_in->shortname, sched_in->comm, sched_in->tid);
|
||||
sched_in->shortname, thread__comm_str(sched_in), sched_in->tid);
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
|
@ -229,6 +229,24 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_print_ip_opts(struct perf_event_attr *attr)
|
||||
{
|
||||
unsigned int type = attr->type;
|
||||
|
||||
output[type].print_ip_opts = 0;
|
||||
if (PRINT_FIELD(IP))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_IP;
|
||||
|
||||
if (PRINT_FIELD(SYM))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_SYM;
|
||||
|
||||
if (PRINT_FIELD(DSO))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_DSO;
|
||||
|
||||
if (PRINT_FIELD(SYMOFFSET))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify all user requested events exist and the samples
|
||||
* have the expected data
|
||||
@ -237,7 +255,6 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
{
|
||||
int j;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_event_attr *attr;
|
||||
|
||||
for (j = 0; j < PERF_TYPE_MAX; ++j) {
|
||||
evsel = perf_session__find_first_evtype(session, j);
|
||||
@ -260,20 +277,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
if (evsel == NULL)
|
||||
continue;
|
||||
|
||||
attr = &evsel->attr;
|
||||
|
||||
output[j].print_ip_opts = 0;
|
||||
if (PRINT_FIELD(IP))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_IP;
|
||||
|
||||
if (PRINT_FIELD(SYM))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_SYM;
|
||||
|
||||
if (PRINT_FIELD(DSO))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_DSO;
|
||||
|
||||
if (PRINT_FIELD(SYMOFFSET))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET;
|
||||
set_print_ip_opts(&evsel->attr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -291,11 +295,11 @@ static void print_sample_start(struct perf_sample *sample,
|
||||
|
||||
if (PRINT_FIELD(COMM)) {
|
||||
if (latency_format)
|
||||
printf("%8.8s ", thread->comm);
|
||||
printf("%8.8s ", thread__comm_str(thread));
|
||||
else if (PRINT_FIELD(IP) && symbol_conf.use_callchain)
|
||||
printf("%s ", thread->comm);
|
||||
printf("%s ", thread__comm_str(thread));
|
||||
else
|
||||
printf("%16s ", thread->comm);
|
||||
printf("%16s ", thread__comm_str(thread));
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(PID) && PRINT_FIELD(TID))
|
||||
@ -547,6 +551,34 @@ struct perf_script {
|
||||
struct perf_session *session;
|
||||
};
|
||||
|
||||
static int process_attr(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_evlist **pevlist)
|
||||
{
|
||||
struct perf_script *scr = container_of(tool, struct perf_script, tool);
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_evsel *evsel, *pos;
|
||||
int err;
|
||||
|
||||
err = perf_event__process_attr(tool, event, pevlist);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
evlist = *pevlist;
|
||||
evsel = perf_evlist__last(*pevlist);
|
||||
|
||||
if (evsel->attr.type >= PERF_TYPE_MAX)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (pos->attr.type == evsel->attr.type && pos != evsel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
set_print_ip_opts(&evsel->attr);
|
||||
|
||||
return perf_evsel__check_attr(evsel, scr->session);
|
||||
}
|
||||
|
||||
static void sig_handler(int sig __maybe_unused)
|
||||
{
|
||||
session_done = 1;
|
||||
@ -1272,7 +1304,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.attr = perf_event__process_attr,
|
||||
.attr = process_attr,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
|
@ -1596,7 +1596,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"perf stat [<options>] [<command>]",
|
||||
NULL
|
||||
};
|
||||
int status = -ENOMEM, run_idx;
|
||||
int status = -EINVAL, run_idx;
|
||||
const char *mode;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
@ -1614,12 +1614,15 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
if (output_name && output_fd) {
|
||||
fprintf(stderr, "cannot use both --output and --log-fd\n");
|
||||
usage_with_options(stat_usage, options);
|
||||
parse_options_usage(stat_usage, options, "o", 1);
|
||||
parse_options_usage(NULL, options, "log-fd", 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (output_fd < 0) {
|
||||
fprintf(stderr, "argument to --log-fd must be a > 0\n");
|
||||
usage_with_options(stat_usage, options);
|
||||
parse_options_usage(stat_usage, options, "log-fd", 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!output) {
|
||||
@ -1656,7 +1659,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
/* User explicitly passed -B? */
|
||||
if (big_num_opt == 1) {
|
||||
fprintf(stderr, "-B option not supported with -x\n");
|
||||
usage_with_options(stat_usage, options);
|
||||
parse_options_usage(stat_usage, options, "B", 1);
|
||||
parse_options_usage(NULL, options, "x", 1);
|
||||
goto out;
|
||||
} else /* Nope, so disable big number formatting */
|
||||
big_num = false;
|
||||
} else if (big_num_opt == 0) /* User passed --no-big-num */
|
||||
@ -1666,7 +1671,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
usage_with_options(stat_usage, options);
|
||||
|
||||
if (run_count < 0) {
|
||||
usage_with_options(stat_usage, options);
|
||||
pr_err("Run count must be a positive number\n");
|
||||
parse_options_usage(stat_usage, options, "r", 1);
|
||||
goto out;
|
||||
} else if (run_count == 0) {
|
||||
forever = true;
|
||||
run_count = 1;
|
||||
@ -1678,8 +1685,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
fprintf(stderr, "both cgroup and no-aggregation "
|
||||
"modes only available in system-wide mode\n");
|
||||
|
||||
usage_with_options(stat_usage, options);
|
||||
return -1;
|
||||
parse_options_usage(stat_usage, options, "G", 1);
|
||||
parse_options_usage(NULL, options, "A", 1);
|
||||
parse_options_usage(NULL, options, "a", 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (add_default_attributes())
|
||||
@ -1688,25 +1697,28 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
perf_target__validate(&target);
|
||||
|
||||
if (perf_evlist__create_maps(evsel_list, &target) < 0) {
|
||||
if (perf_target__has_task(&target))
|
||||
if (perf_target__has_task(&target)) {
|
||||
pr_err("Problems finding threads of monitor\n");
|
||||
if (perf_target__has_cpu(&target))
|
||||
parse_options_usage(stat_usage, options, "p", 1);
|
||||
parse_options_usage(NULL, options, "t", 1);
|
||||
} else if (perf_target__has_cpu(&target)) {
|
||||
perror("failed to parse CPUs map");
|
||||
|
||||
usage_with_options(stat_usage, options);
|
||||
return -1;
|
||||
parse_options_usage(stat_usage, options, "C", 1);
|
||||
parse_options_usage(NULL, options, "a", 1);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
if (interval && interval < 100) {
|
||||
pr_err("print interval must be >= 100ms\n");
|
||||
usage_with_options(stat_usage, options);
|
||||
return -1;
|
||||
parse_options_usage(stat_usage, options, "I", 1);
|
||||
goto out_free_maps;
|
||||
}
|
||||
|
||||
if (perf_evlist__alloc_stats(evsel_list, interval))
|
||||
goto out_free_maps;
|
||||
|
||||
if (perf_stat_init_aggr_mode())
|
||||
goto out;
|
||||
goto out_free_maps;
|
||||
|
||||
/*
|
||||
* We dont want to block the signals - that would cause
|
||||
|
@ -856,7 +856,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||
&sample, machine);
|
||||
} else if (event->header.type < PERF_RECORD_MAX) {
|
||||
hists__inc_nr_events(&evsel->hists, event->header.type);
|
||||
machine__process_event(machine, event);
|
||||
machine__process_event(machine, event, &sample);
|
||||
} else
|
||||
++session->stats.nr_unknown_events;
|
||||
next_event:
|
||||
@ -1040,7 +1040,7 @@ parse_percent_limit(const struct option *opt, const char *arg,
|
||||
|
||||
int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
int status;
|
||||
int status = -1;
|
||||
char errbuf[BUFSIZ];
|
||||
struct perf_top top = {
|
||||
.count_filter = 5,
|
||||
@ -1159,8 +1159,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (sort_order == default_sort_order)
|
||||
sort_order = "dso,symbol";
|
||||
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(top_usage, options);
|
||||
if (setup_sorting() < 0) {
|
||||
parse_options_usage(top_usage, options, "s", 1);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/* display thread wants entries to be collapsed in a different tree */
|
||||
sort__need_collapse = 1;
|
||||
|
@ -1114,7 +1114,7 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre
|
||||
|
||||
if (trace->multiple_threads) {
|
||||
if (trace->show_comm)
|
||||
printed += fprintf(fp, "%.14s/", thread->comm);
|
||||
printed += fprintf(fp, "%.14s/", thread__comm_str(thread));
|
||||
printed += fprintf(fp, "%d ", thread->tid);
|
||||
}
|
||||
|
||||
@ -1122,7 +1122,7 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre
|
||||
}
|
||||
|
||||
static int trace__process_event(struct trace *trace, struct machine *machine,
|
||||
union perf_event *event)
|
||||
union perf_event *event, struct perf_sample *sample)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@ -1130,9 +1130,9 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
|
||||
case PERF_RECORD_LOST:
|
||||
color_fprintf(trace->output, PERF_COLOR_RED,
|
||||
"LOST %" PRIu64 " events!\n", event->lost.lost);
|
||||
ret = machine__process_lost_event(machine, event);
|
||||
ret = machine__process_lost_event(machine, event, sample);
|
||||
default:
|
||||
ret = machine__process_event(machine, event);
|
||||
ret = machine__process_event(machine, event, sample);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1141,11 +1141,11 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
|
||||
|
||||
static int trace__tool_process(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct trace *trace = container_of(tool, struct trace, tool);
|
||||
return trace__process_event(trace, machine, event);
|
||||
return trace__process_event(trace, machine, event, sample);
|
||||
}
|
||||
|
||||
static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
|
||||
@ -1751,7 +1751,7 @@ again:
|
||||
trace->base_time = sample.time;
|
||||
|
||||
if (type != PERF_RECORD_SAMPLE) {
|
||||
trace__process_event(trace, trace->host, event);
|
||||
trace__process_event(trace, trace->host, event, &sample);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1986,7 +1986,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
|
||||
else if (ratio > 5.0)
|
||||
color = PERF_COLOR_YELLOW;
|
||||
|
||||
printed += color_fprintf(fp, color, "%20s", thread->comm);
|
||||
printed += color_fprintf(fp, color, "%20s", thread__comm_str(thread));
|
||||
printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events);
|
||||
printed += color_fprintf(fp, color, "%5.1f%%", ratio);
|
||||
printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms);
|
||||
|
@ -25,9 +25,11 @@ ifeq ($(ARCH),x86_64)
|
||||
RAW_ARCH := x86_64
|
||||
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT
|
||||
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
|
||||
LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
|
||||
else
|
||||
LIBUNWIND_LIBS = -lunwind -lunwind-x86
|
||||
endif
|
||||
NO_PERF_REGS := 0
|
||||
LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
|
||||
endif
|
||||
|
||||
ifeq ($(NO_PERF_REGS),0)
|
||||
@ -96,7 +98,7 @@ endif
|
||||
|
||||
feature_check = $(eval $(feature_check_code))
|
||||
define feature_check_code
|
||||
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) LDFLAGS=$(LDFLAGS) -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0)
|
||||
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) LIBUNWIND_LIBS="$(LIBUNWIND_LIBS)" -C config/feature-checks test-$1 >/dev/null 2>/dev/null && echo 1 || echo 0)
|
||||
endef
|
||||
|
||||
feature_set = $(eval $(feature_set_code))
|
||||
@ -173,7 +175,7 @@ ifeq ($(feature-all), 1)
|
||||
#
|
||||
$(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_set,$(feat)))
|
||||
else
|
||||
$(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1)
|
||||
$(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(CORE_FEATURE_TESTS) >/dev/null 2>&1)
|
||||
$(foreach feat,$(CORE_FEATURE_TESTS),$(call feature_check,$(feat)))
|
||||
endif
|
||||
|
||||
|
@ -31,12 +31,12 @@ CC := $(CC) -MD
|
||||
|
||||
all: $(FILES)
|
||||
|
||||
BUILD = $(CC) $(LDFLAGS) -o $(OUTPUT)$@ $@.c
|
||||
BUILD = $(CC) $(CFLAGS) $(LDFLAGS) -o $(OUTPUT)$@ $@.c
|
||||
|
||||
###############################
|
||||
|
||||
test-all:
|
||||
$(BUILD) -Werror -fstack-protector -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lunwind -lunwind-x86_64 -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl
|
||||
$(BUILD) -Werror -fstack-protector -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma $(LIBUNWIND_LIBS) -lelf -laudit -I/usr/include/slang -lslang $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl
|
||||
|
||||
test-hello:
|
||||
$(BUILD)
|
||||
@ -72,7 +72,7 @@ test-libnuma:
|
||||
$(BUILD) -lnuma
|
||||
|
||||
test-libunwind:
|
||||
$(BUILD) -lunwind -lunwind-x86_64 -lelf
|
||||
$(BUILD) $(LIBUNWIND_LIBS) -lelf
|
||||
|
||||
test-libaudit:
|
||||
$(BUILD) -laudit
|
||||
|
@ -276,7 +276,7 @@ static int process_event(struct machine *machine, struct perf_evlist *evlist,
|
||||
return process_sample_event(machine, evlist, event, state);
|
||||
|
||||
if (event->header.type < PERF_RECORD_MAX)
|
||||
return machine__process_event(machine, event);
|
||||
return machine__process_event(machine, event, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ static struct machine *setup_fake_machine(struct machines *machines)
|
||||
if (thread == NULL)
|
||||
goto out;
|
||||
|
||||
thread__set_comm(thread, fake_threads[i].comm);
|
||||
thread__set_comm(thread, fake_threads[i].comm, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) {
|
||||
@ -110,7 +110,7 @@ static struct machine *setup_fake_machine(struct machines *machines)
|
||||
strcpy(fake_mmap_event.mmap.filename,
|
||||
fake_mmap_info[i].filename);
|
||||
|
||||
machine__process_mmap_event(machine, &fake_mmap_event);
|
||||
machine__process_mmap_event(machine, &fake_mmap_event, NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) {
|
||||
@ -421,7 +421,7 @@ static void print_hists(struct hists *hists)
|
||||
he = rb_entry(node, struct hist_entry, rb_node_in);
|
||||
|
||||
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
|
||||
i, he->thread->comm, he->ms.map->dso->short_name,
|
||||
i, thread__comm_str(he->thread), he->ms.map->dso->short_name,
|
||||
he->ms.sym->name, he->stat.period);
|
||||
|
||||
i++;
|
||||
|
@ -121,6 +121,9 @@ static bool samples_same(const struct perf_sample *s1,
|
||||
if (type & PERF_SAMPLE_DATA_SRC)
|
||||
COMP(data_src);
|
||||
|
||||
if (type & PERF_SAMPLE_TRANSACTION)
|
||||
COMP(transaction);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -165,6 +168,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
|
||||
.cpu = 110,
|
||||
.raw_size = sizeof(raw_data),
|
||||
.data_src = 111,
|
||||
.transaction = 112,
|
||||
.raw_data = (void *)raw_data,
|
||||
.callchain = &callchain.callchain,
|
||||
.branch_stack = &branch_stack.branch_stack,
|
||||
@ -273,7 +277,8 @@ int test__sample_parsing(void)
|
||||
|
||||
/*
|
||||
* Fail the test if it has not been updated when new sample format bits
|
||||
* were added.
|
||||
* were added. Please actually update the test rather than just change
|
||||
* the condition below.
|
||||
*/
|
||||
if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) {
|
||||
pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n");
|
||||
|
@ -1255,7 +1255,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
|
||||
if (thread)
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
", Thread: %s(%d)",
|
||||
(thread->comm_set ? thread->comm : ""),
|
||||
(thread->comm_set ? thread__comm_str(thread) : ""),
|
||||
thread->tid);
|
||||
if (dso)
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
@ -1578,7 +1578,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
if (thread != NULL &&
|
||||
asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
|
||||
(browser->hists->thread_filter ? "out of" : "into"),
|
||||
(thread->comm_set ? thread->comm : ""),
|
||||
(thread->comm_set ? thread__comm_str(thread) : ""),
|
||||
thread->tid) > 0)
|
||||
zoom_thread = nr_options++;
|
||||
|
||||
@ -1598,7 +1598,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
struct symbol *sym;
|
||||
|
||||
if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
|
||||
browser->he_selection->thread->comm) > 0)
|
||||
thread__comm_str(browser->he_selection->thread)) > 0)
|
||||
scripts_comm = nr_options++;
|
||||
|
||||
sym = browser->he_selection->ms.sym;
|
||||
@ -1701,7 +1701,7 @@ zoom_out_thread:
|
||||
sort_thread.elide = false;
|
||||
} else {
|
||||
ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
|
||||
thread->comm_set ? thread->comm : "",
|
||||
thread->comm_set ? thread__comm_str(thread) : "",
|
||||
thread->tid);
|
||||
browser->hists->thread_filter = thread;
|
||||
sort_thread.elide = true;
|
||||
@ -1717,7 +1717,7 @@ do_scripts:
|
||||
memset(script_opt, 0, 64);
|
||||
|
||||
if (choice == scripts_comm)
|
||||
sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
|
||||
sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
|
||||
|
||||
if (choice == scripts_symbol)
|
||||
sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
|
||||
|
121
tools/perf/util/comm.c
Normal file
121
tools/perf/util/comm.c
Normal file
@ -0,0 +1,121 @@
|
||||
#include "comm.h"
|
||||
#include "util.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct comm_str {
|
||||
char *str;
|
||||
struct rb_node rb_node;
|
||||
int ref;
|
||||
};
|
||||
|
||||
/* Should perhaps be moved to struct machine */
|
||||
static struct rb_root comm_str_root;
|
||||
|
||||
static void comm_str__get(struct comm_str *cs)
|
||||
{
|
||||
cs->ref++;
|
||||
}
|
||||
|
||||
static void comm_str__put(struct comm_str *cs)
|
||||
{
|
||||
if (!--cs->ref) {
|
||||
rb_erase(&cs->rb_node, &comm_str_root);
|
||||
free(cs->str);
|
||||
free(cs);
|
||||
}
|
||||
}
|
||||
|
||||
static struct comm_str *comm_str__alloc(const char *str)
|
||||
{
|
||||
struct comm_str *cs;
|
||||
|
||||
cs = zalloc(sizeof(*cs));
|
||||
if (!cs)
|
||||
return NULL;
|
||||
|
||||
cs->str = strdup(str);
|
||||
if (!cs->str) {
|
||||
free(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct comm_str *iter, *new;
|
||||
int cmp;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct comm_str, rb_node);
|
||||
|
||||
cmp = strcmp(str, iter->str);
|
||||
if (!cmp)
|
||||
return iter;
|
||||
|
||||
if (cmp < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
new = comm_str__alloc(str);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
rb_link_node(&new->rb_node, parent, p);
|
||||
rb_insert_color(&new->rb_node, root);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct comm *comm__new(const char *str, u64 timestamp)
|
||||
{
|
||||
struct comm *comm = zalloc(sizeof(*comm));
|
||||
|
||||
if (!comm)
|
||||
return NULL;
|
||||
|
||||
comm->start = timestamp;
|
||||
|
||||
comm->comm_str = comm_str__findnew(str, &comm_str_root);
|
||||
if (!comm->comm_str) {
|
||||
free(comm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
comm_str__get(comm->comm_str);
|
||||
|
||||
return comm;
|
||||
}
|
||||
|
||||
void comm__override(struct comm *comm, const char *str, u64 timestamp)
|
||||
{
|
||||
struct comm_str *old = comm->comm_str;
|
||||
|
||||
comm->comm_str = comm_str__findnew(str, &comm_str_root);
|
||||
if (!comm->comm_str) {
|
||||
comm->comm_str = old;
|
||||
return;
|
||||
}
|
||||
|
||||
comm->start = timestamp;
|
||||
comm_str__get(comm->comm_str);
|
||||
comm_str__put(old);
|
||||
}
|
||||
|
||||
void comm__free(struct comm *comm)
|
||||
{
|
||||
comm_str__put(comm->comm_str);
|
||||
free(comm);
|
||||
}
|
||||
|
||||
const char *comm__str(const struct comm *comm)
|
||||
{
|
||||
return comm->comm_str->str;
|
||||
}
|
21
tools/perf/util/comm.h
Normal file
21
tools/perf/util/comm.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef __PERF_COMM_H
|
||||
#define __PERF_COMM_H
|
||||
|
||||
#include "../perf.h"
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
struct comm_str;
|
||||
|
||||
struct comm {
|
||||
struct comm_str *comm_str;
|
||||
u64 start;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
void comm__free(struct comm *comm);
|
||||
struct comm *comm__new(const char *str, u64 timestamp);
|
||||
const char *comm__str(const struct comm *comm);
|
||||
void comm__override(struct comm *comm, const char *str, u64 timestamp);
|
||||
|
||||
#endif /* __PERF_COMM_H */
|
@ -512,18 +512,18 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
|
||||
|
||||
int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_comm_event(machine, event);
|
||||
return machine__process_comm_event(machine, event, sample);
|
||||
}
|
||||
|
||||
int perf_event__process_lost(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_lost_event(machine, event);
|
||||
return machine__process_lost_event(machine, event, sample);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
|
||||
@ -546,18 +546,18 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
|
||||
|
||||
int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_mmap_event(machine, event);
|
||||
return machine__process_mmap_event(machine, event, sample);
|
||||
}
|
||||
|
||||
int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_mmap2_event(machine, event);
|
||||
return machine__process_mmap2_event(machine, event, sample);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
|
||||
@ -569,18 +569,18 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
|
||||
|
||||
int perf_event__process_fork(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_fork_event(machine, event);
|
||||
return machine__process_fork_event(machine, event, sample);
|
||||
}
|
||||
|
||||
int perf_event__process_exit(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_exit_event(machine, event);
|
||||
return machine__process_exit_event(machine, event, sample);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
||||
@ -611,10 +611,10 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
||||
|
||||
int perf_event__process(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_event(machine, event);
|
||||
return machine__process_event(machine, event, sample);
|
||||
}
|
||||
|
||||
void thread__find_addr_map(struct thread *self,
|
||||
@ -721,10 +721,10 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||
return -1;
|
||||
|
||||
if (symbol_conf.comm_list &&
|
||||
!strlist__has_entry(symbol_conf.comm_list, thread->comm))
|
||||
!strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread)))
|
||||
goto out_filtered;
|
||||
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid);
|
||||
dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
|
||||
/*
|
||||
* Have we already created the kernel maps for this machine?
|
||||
*
|
||||
|
@ -607,6 +607,8 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist,
|
||||
evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot,
|
||||
MAP_SHARED, fd, 0);
|
||||
if (evlist->mmap[idx].base == MAP_FAILED) {
|
||||
pr_debug2("failed to mmap perf event ring buffer, error %d\n",
|
||||
errno);
|
||||
evlist->mmap[idx].base = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
@ -663,7 +663,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
}
|
||||
|
||||
if (opts->sample_address)
|
||||
attr->sample_type |= PERF_SAMPLE_DATA_SRC;
|
||||
perf_evsel__set_sample_bit(evsel, DATA_SRC);
|
||||
|
||||
if (opts->no_delay) {
|
||||
attr->watermark = 0;
|
||||
@ -675,13 +675,13 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
}
|
||||
|
||||
if (opts->sample_weight)
|
||||
attr->sample_type |= PERF_SAMPLE_WEIGHT;
|
||||
perf_evsel__set_sample_bit(evsel, WEIGHT);
|
||||
|
||||
attr->mmap = track;
|
||||
attr->comm = track;
|
||||
|
||||
if (opts->sample_transaction)
|
||||
attr->sample_type |= PERF_SAMPLE_TRANSACTION;
|
||||
perf_evsel__set_sample_bit(evsel, TRANSACTION);
|
||||
|
||||
/*
|
||||
* XXX see the function comment above
|
||||
@ -1051,6 +1051,8 @@ retry_open:
|
||||
group_fd, flags);
|
||||
if (FD(evsel, cpu, thread) < 0) {
|
||||
err = -errno;
|
||||
pr_debug2("perf_event_open failed, error %d\n",
|
||||
err);
|
||||
goto try_fallback;
|
||||
}
|
||||
set_rlimit = NO_CHANGE;
|
||||
@ -1479,6 +1481,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||
|
||||
data->transaction = 0;
|
||||
if (type & PERF_SAMPLE_TRANSACTION) {
|
||||
OVERFLOW_CHECK_u64(array);
|
||||
data->transaction = *array;
|
||||
array++;
|
||||
}
|
||||
@ -1575,6 +1578,9 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||
if (type & PERF_SAMPLE_DATA_SRC)
|
||||
result += sizeof(u64);
|
||||
|
||||
if (type & PERF_SAMPLE_TRANSACTION)
|
||||
result += sizeof(u64);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1748,6 +1754,11 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||
array++;
|
||||
}
|
||||
|
||||
if (type & PERF_SAMPLE_TRANSACTION) {
|
||||
*array = sample->transaction;
|
||||
array++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -416,6 +416,7 @@ struct hist_entry *__hists__add_mem_entry(struct hists *hists,
|
||||
{
|
||||
struct hist_entry entry = {
|
||||
.thread = al->thread,
|
||||
.comm = thread__comm(al->thread),
|
||||
.ms = {
|
||||
.map = al->map,
|
||||
.sym = al->sym,
|
||||
@ -446,6 +447,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *hists,
|
||||
{
|
||||
struct hist_entry entry = {
|
||||
.thread = al->thread,
|
||||
.comm = thread__comm(al->thread),
|
||||
.ms = {
|
||||
.map = bi->to.map,
|
||||
.sym = bi->to.sym,
|
||||
@ -475,6 +477,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
|
||||
{
|
||||
struct hist_entry entry = {
|
||||
.thread = al->thread,
|
||||
.comm = thread__comm(al->thread),
|
||||
.ms = {
|
||||
.map = al->map,
|
||||
.sym = al->sym,
|
||||
|
@ -40,7 +40,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(comm, sizeof(comm), "[guest/%d]", pid);
|
||||
thread__set_comm(thread, comm);
|
||||
thread__set_comm(thread, comm, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -331,7 +331,8 @@ struct thread *machine__find_thread(struct machine *machine, pid_t tid)
|
||||
return __machine__findnew_thread(machine, 0, tid, false);
|
||||
}
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event)
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine,
|
||||
event->comm.pid,
|
||||
@ -340,7 +341,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_comm(event, stdout);
|
||||
|
||||
if (thread == NULL || thread__set_comm(thread, event->comm.comm)) {
|
||||
if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) {
|
||||
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
|
||||
return -1;
|
||||
}
|
||||
@ -349,7 +350,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event
|
||||
}
|
||||
|
||||
int machine__process_lost_event(struct machine *machine __maybe_unused,
|
||||
union perf_event *event)
|
||||
union perf_event *event, struct perf_sample *sample __maybe_unused)
|
||||
{
|
||||
dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
|
||||
event->lost.id, event->lost.lost);
|
||||
@ -984,7 +985,8 @@ out_problem:
|
||||
}
|
||||
|
||||
int machine__process_mmap2_event(struct machine *machine,
|
||||
union perf_event *event)
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused)
|
||||
{
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread;
|
||||
@ -1031,7 +1033,8 @@ out_problem:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event)
|
||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused)
|
||||
{
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread;
|
||||
@ -1088,7 +1091,8 @@ static void machine__remove_thread(struct machine *machine, struct thread *th)
|
||||
list_add_tail(&th->node, &machine->dead_threads);
|
||||
}
|
||||
|
||||
int machine__process_fork_event(struct machine *machine, union perf_event *event)
|
||||
int machine__process_fork_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct thread *thread = machine__find_thread(machine, event->fork.tid);
|
||||
struct thread *parent = machine__findnew_thread(machine,
|
||||
@ -1105,7 +1109,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
|
||||
perf_event__fprintf_task(event, stdout);
|
||||
|
||||
if (thread == NULL || parent == NULL ||
|
||||
thread__fork(thread, parent) < 0) {
|
||||
thread__fork(thread, parent, sample->time) < 0) {
|
||||
dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
|
||||
return -1;
|
||||
}
|
||||
@ -1113,8 +1117,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine__process_exit_event(struct machine *machine __maybe_unused,
|
||||
union perf_event *event)
|
||||
int machine__process_exit_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused)
|
||||
{
|
||||
struct thread *thread = machine__find_thread(machine, event->fork.tid);
|
||||
|
||||
@ -1127,23 +1131,24 @@ int machine__process_exit_event(struct machine *machine __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine__process_event(struct machine *machine, union perf_event *event)
|
||||
int machine__process_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_COMM:
|
||||
ret = machine__process_comm_event(machine, event); break;
|
||||
ret = machine__process_comm_event(machine, event, sample); break;
|
||||
case PERF_RECORD_MMAP:
|
||||
ret = machine__process_mmap_event(machine, event); break;
|
||||
ret = machine__process_mmap_event(machine, event, sample); break;
|
||||
case PERF_RECORD_MMAP2:
|
||||
ret = machine__process_mmap2_event(machine, event); break;
|
||||
ret = machine__process_mmap2_event(machine, event, sample); break;
|
||||
case PERF_RECORD_FORK:
|
||||
ret = machine__process_fork_event(machine, event); break;
|
||||
ret = machine__process_fork_event(machine, event, sample); break;
|
||||
case PERF_RECORD_EXIT:
|
||||
ret = machine__process_exit_event(machine, event); break;
|
||||
ret = machine__process_exit_event(machine, event, sample); break;
|
||||
case PERF_RECORD_LOST:
|
||||
ret = machine__process_lost_event(machine, event); break;
|
||||
ret = machine__process_lost_event(machine, event, sample); break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
|
@ -40,13 +40,20 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)
|
||||
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t tid);
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_exit_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_fork_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_lost_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_mmap2_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample);
|
||||
int machine__process_exit_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample);
|
||||
int machine__process_fork_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample);
|
||||
int machine__process_lost_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample);
|
||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample);
|
||||
int machine__process_mmap2_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample);
|
||||
int machine__process_event(struct machine *machine, union perf_event *event,
|
||||
struct perf_sample *sample);
|
||||
|
||||
typedef void (*machine__process_t)(struct machine *machine, void *data);
|
||||
|
||||
|
@ -339,10 +339,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
if (arg[1] != '-') {
|
||||
ctx->opt = arg + 1;
|
||||
if (internal_help && *ctx->opt == 'h')
|
||||
return parse_options_usage(usagestr, options);
|
||||
return usage_with_options_internal(usagestr, options, 0);
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
return parse_options_usage(usagestr, options, arg + 1, 1);
|
||||
case -2:
|
||||
goto unknown;
|
||||
default:
|
||||
@ -352,10 +352,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
check_typos(arg + 1, options);
|
||||
while (ctx->opt) {
|
||||
if (internal_help && *ctx->opt == 'h')
|
||||
return parse_options_usage(usagestr, options);
|
||||
return usage_with_options_internal(usagestr, options, 0);
|
||||
arg = ctx->opt;
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
return parse_options_usage(usagestr, options, arg, 1);
|
||||
case -2:
|
||||
/* fake a short option thing to hide the fact that we may have
|
||||
* started to parse aggregated stuff
|
||||
@ -383,12 +384,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
if (internal_help && !strcmp(arg + 2, "help-all"))
|
||||
return usage_with_options_internal(usagestr, options, 1);
|
||||
if (internal_help && !strcmp(arg + 2, "help"))
|
||||
return parse_options_usage(usagestr, options);
|
||||
return usage_with_options_internal(usagestr, options, 0);
|
||||
if (!strcmp(arg + 2, "list-opts"))
|
||||
return PARSE_OPT_LIST;
|
||||
switch (parse_long_opt(ctx, arg + 2, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
return parse_options_usage(usagestr, options, arg + 2, 0);
|
||||
case -2:
|
||||
goto unknown;
|
||||
default:
|
||||
@ -445,6 +446,89 @@ int parse_options(int argc, const char **argv, const struct option *options,
|
||||
#define USAGE_OPTS_WIDTH 24
|
||||
#define USAGE_GAP 2
|
||||
|
||||
static void print_option_help(const struct option *opts, int full)
|
||||
{
|
||||
size_t pos;
|
||||
int pad;
|
||||
|
||||
if (opts->type == OPTION_GROUP) {
|
||||
fputc('\n', stderr);
|
||||
if (*opts->help)
|
||||
fprintf(stderr, "%s\n", opts->help);
|
||||
return;
|
||||
}
|
||||
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
|
||||
return;
|
||||
|
||||
pos = fprintf(stderr, " ");
|
||||
if (opts->short_name)
|
||||
pos += fprintf(stderr, "-%c", opts->short_name);
|
||||
else
|
||||
pos += fprintf(stderr, " ");
|
||||
|
||||
if (opts->long_name && opts->short_name)
|
||||
pos += fprintf(stderr, ", ");
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "--%s", opts->long_name);
|
||||
|
||||
switch (opts->type) {
|
||||
case OPTION_ARGUMENT:
|
||||
break;
|
||||
case OPTION_LONG:
|
||||
case OPTION_U64:
|
||||
case OPTION_INTEGER:
|
||||
case OPTION_UINTEGER:
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=<n>]");
|
||||
else
|
||||
pos += fprintf(stderr, "[<n>]");
|
||||
else
|
||||
pos += fprintf(stderr, " <n>");
|
||||
break;
|
||||
case OPTION_CALLBACK:
|
||||
if (opts->flags & PARSE_OPT_NOARG)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
case OPTION_STRING:
|
||||
if (opts->argh) {
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=<%s>]", opts->argh);
|
||||
else
|
||||
pos += fprintf(stderr, "[<%s>]", opts->argh);
|
||||
else
|
||||
pos += fprintf(stderr, " <%s>", opts->argh);
|
||||
} else {
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=...]");
|
||||
else
|
||||
pos += fprintf(stderr, "[...]");
|
||||
else
|
||||
pos += fprintf(stderr, " ...");
|
||||
}
|
||||
break;
|
||||
default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
|
||||
case OPTION_END:
|
||||
case OPTION_GROUP:
|
||||
case OPTION_BIT:
|
||||
case OPTION_BOOLEAN:
|
||||
case OPTION_INCR:
|
||||
case OPTION_SET_UINT:
|
||||
case OPTION_SET_PTR:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos <= USAGE_OPTS_WIDTH)
|
||||
pad = USAGE_OPTS_WIDTH - pos;
|
||||
else {
|
||||
fputc('\n', stderr);
|
||||
pad = USAGE_OPTS_WIDTH;
|
||||
}
|
||||
fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
|
||||
}
|
||||
|
||||
int usage_with_options_internal(const char * const *usagestr,
|
||||
const struct option *opts, int full)
|
||||
{
|
||||
@ -464,87 +548,9 @@ int usage_with_options_internal(const char * const *usagestr,
|
||||
if (opts->type != OPTION_GROUP)
|
||||
fputc('\n', stderr);
|
||||
|
||||
for (; opts->type != OPTION_END; opts++) {
|
||||
size_t pos;
|
||||
int pad;
|
||||
for ( ; opts->type != OPTION_END; opts++)
|
||||
print_option_help(opts, full);
|
||||
|
||||
if (opts->type == OPTION_GROUP) {
|
||||
fputc('\n', stderr);
|
||||
if (*opts->help)
|
||||
fprintf(stderr, "%s\n", opts->help);
|
||||
continue;
|
||||
}
|
||||
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
|
||||
continue;
|
||||
|
||||
pos = fprintf(stderr, " ");
|
||||
if (opts->short_name)
|
||||
pos += fprintf(stderr, "-%c", opts->short_name);
|
||||
else
|
||||
pos += fprintf(stderr, " ");
|
||||
|
||||
if (opts->long_name && opts->short_name)
|
||||
pos += fprintf(stderr, ", ");
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "--%s", opts->long_name);
|
||||
|
||||
switch (opts->type) {
|
||||
case OPTION_ARGUMENT:
|
||||
break;
|
||||
case OPTION_LONG:
|
||||
case OPTION_U64:
|
||||
case OPTION_INTEGER:
|
||||
case OPTION_UINTEGER:
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=<n>]");
|
||||
else
|
||||
pos += fprintf(stderr, "[<n>]");
|
||||
else
|
||||
pos += fprintf(stderr, " <n>");
|
||||
break;
|
||||
case OPTION_CALLBACK:
|
||||
if (opts->flags & PARSE_OPT_NOARG)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
case OPTION_STRING:
|
||||
if (opts->argh) {
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=<%s>]", opts->argh);
|
||||
else
|
||||
pos += fprintf(stderr, "[<%s>]", opts->argh);
|
||||
else
|
||||
pos += fprintf(stderr, " <%s>", opts->argh);
|
||||
} else {
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=...]");
|
||||
else
|
||||
pos += fprintf(stderr, "[...]");
|
||||
else
|
||||
pos += fprintf(stderr, " ...");
|
||||
}
|
||||
break;
|
||||
default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
|
||||
case OPTION_END:
|
||||
case OPTION_GROUP:
|
||||
case OPTION_BIT:
|
||||
case OPTION_BOOLEAN:
|
||||
case OPTION_INCR:
|
||||
case OPTION_SET_UINT:
|
||||
case OPTION_SET_PTR:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos <= USAGE_OPTS_WIDTH)
|
||||
pad = USAGE_OPTS_WIDTH - pos;
|
||||
else {
|
||||
fputc('\n', stderr);
|
||||
pad = USAGE_OPTS_WIDTH;
|
||||
}
|
||||
fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
|
||||
return PARSE_OPT_HELP;
|
||||
@ -559,9 +565,45 @@ void usage_with_options(const char * const *usagestr,
|
||||
}
|
||||
|
||||
int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
const struct option *opts,
|
||||
const char *optstr, bool short_opt)
|
||||
{
|
||||
return usage_with_options_internal(usagestr, opts, 0);
|
||||
if (!usagestr)
|
||||
goto opt;
|
||||
|
||||
fprintf(stderr, "\n usage: %s\n", *usagestr++);
|
||||
while (*usagestr && **usagestr)
|
||||
fprintf(stderr, " or: %s\n", *usagestr++);
|
||||
while (*usagestr) {
|
||||
fprintf(stderr, "%s%s\n",
|
||||
**usagestr ? " " : "",
|
||||
*usagestr);
|
||||
usagestr++;
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
|
||||
opt:
|
||||
for ( ; opts->type != OPTION_END; opts++) {
|
||||
if (short_opt) {
|
||||
if (opts->short_name == *optstr)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opts->long_name == NULL)
|
||||
continue;
|
||||
|
||||
if (!prefixcmp(optstr, opts->long_name))
|
||||
break;
|
||||
if (!prefixcmp(optstr, "no-") &&
|
||||
!prefixcmp(optstr + 3, opts->long_name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (opts->type != OPTION_END)
|
||||
print_option_help(opts, 0);
|
||||
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
|
||||
|
||||
|
@ -158,7 +158,9 @@ struct parse_opt_ctx_t {
|
||||
};
|
||||
|
||||
extern int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts);
|
||||
const struct option *opts,
|
||||
const char *optstr,
|
||||
bool short_opt);
|
||||
|
||||
extern void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, int flags);
|
||||
|
@ -583,7 +583,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
|
||||
}
|
||||
|
||||
if (die_find_member(&type, field->name, die_mem) == NULL) {
|
||||
pr_warning("%s(tyep:%s) has no member %s.\n", varname,
|
||||
pr_warning("%s(type:%s) has no member %s.\n", varname,
|
||||
dwarf_diename(&type), field->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
|
||||
int cpu = sample->cpu;
|
||||
void *data = sample->raw_data;
|
||||
unsigned long long nsecs = sample->time;
|
||||
char *comm = thread->comm;
|
||||
const char *comm = thread__comm_str(thread);
|
||||
|
||||
dSP;
|
||||
|
||||
|
@ -250,7 +250,7 @@ static void python_process_tracepoint(union perf_event *perf_event
|
||||
int cpu = sample->cpu;
|
||||
void *data = sample->raw_data;
|
||||
unsigned long long nsecs = sample->time;
|
||||
char *comm = thread->comm;
|
||||
const char *comm = thread__comm_str(thread);
|
||||
|
||||
t = PyTuple_New(MAX_FIELDS);
|
||||
if (!t)
|
||||
@ -389,7 +389,7 @@ static void python_process_general_event(union perf_event *perf_event
|
||||
pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize(
|
||||
(const char *)sample->raw_data, sample->raw_size));
|
||||
pydict_set_item_string_decref(dict, "comm",
|
||||
PyString_FromString(thread->comm));
|
||||
PyString_FromString(thread__comm_str(thread)));
|
||||
if (al->map) {
|
||||
pydict_set_item_string_decref(dict, "dso",
|
||||
PyString_FromString(al->map->dso->name));
|
||||
|
@ -1100,7 +1100,7 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(self, 0);
|
||||
|
||||
if (thread == NULL || thread__set_comm(thread, "swapper")) {
|
||||
if (thread == NULL || thread__set_comm(thread, "swapper", 0)) {
|
||||
pr_err("problem inserting idle task.\n");
|
||||
thread = NULL;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "sort.h"
|
||||
#include "hist.h"
|
||||
#include "comm.h"
|
||||
#include "symbol.h"
|
||||
|
||||
regex_t parent_regex;
|
||||
@ -42,7 +43,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
|
||||
return n;
|
||||
}
|
||||
|
||||
static int64_t cmp_null(void *l, void *r)
|
||||
static int64_t cmp_null(const void *l, const void *r)
|
||||
{
|
||||
if (!l && !r)
|
||||
return 0;
|
||||
@ -63,8 +64,9 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
const char *comm = thread__comm_str(he->thread);
|
||||
return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
|
||||
he->thread->comm ?: "", he->thread->tid);
|
||||
comm ?: "", he->thread->tid);
|
||||
}
|
||||
|
||||
struct sort_entry sort_thread = {
|
||||
@ -79,25 +81,21 @@ struct sort_entry sort_thread = {
|
||||
static int64_t
|
||||
sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return right->thread->tid - left->thread->tid;
|
||||
/* Compare the addr that should be unique among comm */
|
||||
return comm__str(right->comm) - comm__str(left->comm);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
char *comm_l = left->thread->comm;
|
||||
char *comm_r = right->thread->comm;
|
||||
|
||||
if (!comm_l || !comm_r)
|
||||
return cmp_null(comm_l, comm_r);
|
||||
|
||||
return strcmp(comm_l, comm_r);
|
||||
/* Compare the addr that should be unique among comm */
|
||||
return comm__str(right->comm) - comm__str(left->comm);
|
||||
}
|
||||
|
||||
static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
return repsep_snprintf(bf, size, "%*s", width, he->thread->comm);
|
||||
return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm));
|
||||
}
|
||||
|
||||
struct sort_entry sort_comm = {
|
||||
|
@ -84,6 +84,7 @@ struct hist_entry {
|
||||
struct he_stat stat;
|
||||
struct map_symbol ms;
|
||||
struct thread *thread;
|
||||
struct comm *comm;
|
||||
u64 ip;
|
||||
u64 transaction;
|
||||
s32 cpu;
|
||||
|
@ -6,9 +6,12 @@
|
||||
#include "thread.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "comm.h"
|
||||
|
||||
struct thread *thread__new(pid_t pid, pid_t tid)
|
||||
{
|
||||
char *comm_str;
|
||||
struct comm *comm;
|
||||
struct thread *thread = zalloc(sizeof(*thread));
|
||||
|
||||
if (thread != NULL) {
|
||||
@ -16,41 +19,88 @@ struct thread *thread__new(pid_t pid, pid_t tid)
|
||||
thread->pid_ = pid;
|
||||
thread->tid = tid;
|
||||
thread->ppid = -1;
|
||||
thread->comm = malloc(32);
|
||||
if (thread->comm)
|
||||
snprintf(thread->comm, 32, ":%d", thread->tid);
|
||||
INIT_LIST_HEAD(&thread->comm_list);
|
||||
|
||||
comm_str = malloc(32);
|
||||
if (!comm_str)
|
||||
goto err_thread;
|
||||
|
||||
snprintf(comm_str, 32, ":%d", tid);
|
||||
comm = comm__new(comm_str, 0);
|
||||
free(comm_str);
|
||||
if (!comm)
|
||||
goto err_thread;
|
||||
|
||||
list_add(&comm->list, &thread->comm_list);
|
||||
}
|
||||
|
||||
return thread;
|
||||
|
||||
err_thread:
|
||||
free(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void thread__delete(struct thread *thread)
|
||||
{
|
||||
struct comm *comm, *tmp;
|
||||
|
||||
map_groups__exit(&thread->mg);
|
||||
free(thread->comm);
|
||||
list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) {
|
||||
list_del(&comm->list);
|
||||
comm__free(comm);
|
||||
}
|
||||
|
||||
free(thread);
|
||||
}
|
||||
|
||||
int thread__set_comm(struct thread *thread, const char *comm)
|
||||
struct comm *thread__comm(const struct thread *thread)
|
||||
{
|
||||
int err;
|
||||
if (list_empty(&thread->comm_list))
|
||||
return NULL;
|
||||
|
||||
if (thread->comm)
|
||||
free(thread->comm);
|
||||
thread->comm = strdup(comm);
|
||||
err = thread->comm == NULL ? -ENOMEM : 0;
|
||||
if (!err) {
|
||||
thread->comm_set = true;
|
||||
}
|
||||
return err;
|
||||
return list_first_entry(&thread->comm_list, struct comm, list);
|
||||
}
|
||||
|
||||
/* CHECKME: time should always be 0 if event aren't ordered */
|
||||
int thread__set_comm(struct thread *thread, const char *str, u64 timestamp)
|
||||
{
|
||||
struct comm *new, *curr = thread__comm(thread);
|
||||
|
||||
/* Override latest entry if it had no specific time coverage */
|
||||
if (!curr->start) {
|
||||
comm__override(curr, str, timestamp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
new = comm__new(str, timestamp);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
list_add(&new->list, &thread->comm_list);
|
||||
thread->comm_set = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *thread__comm_str(const struct thread *thread)
|
||||
{
|
||||
const struct comm *comm = thread__comm(thread);
|
||||
|
||||
if (!comm)
|
||||
return NULL;
|
||||
|
||||
return comm__str(comm);
|
||||
}
|
||||
|
||||
/* CHECKME: it should probably better return the max comm len from its comm list */
|
||||
int thread__comm_len(struct thread *thread)
|
||||
{
|
||||
if (!thread->comm_len) {
|
||||
if (!thread->comm)
|
||||
const char *comm = thread__comm_str(thread);
|
||||
if (!comm)
|
||||
return 0;
|
||||
thread->comm_len = strlen(thread->comm);
|
||||
thread->comm_len = strlen(comm);
|
||||
}
|
||||
|
||||
return thread->comm_len;
|
||||
@ -58,7 +108,7 @@ int thread__comm_len(struct thread *thread)
|
||||
|
||||
size_t thread__fprintf(struct thread *thread, FILE *fp)
|
||||
{
|
||||
return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) +
|
||||
return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) +
|
||||
map_groups__fprintf(&thread->mg, verbose, fp);
|
||||
}
|
||||
|
||||
@ -68,16 +118,17 @@ void thread__insert_map(struct thread *thread, struct map *map)
|
||||
map_groups__insert(&thread->mg, map);
|
||||
}
|
||||
|
||||
int thread__fork(struct thread *thread, struct thread *parent)
|
||||
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
|
||||
{
|
||||
int i;
|
||||
int i, err;
|
||||
|
||||
if (parent->comm_set) {
|
||||
if (thread->comm)
|
||||
free(thread->comm);
|
||||
thread->comm = strdup(parent->comm);
|
||||
if (!thread->comm)
|
||||
const char *comm = thread__comm_str(parent);
|
||||
if (!comm)
|
||||
return -ENOMEM;
|
||||
err = thread__set_comm(thread, comm, timestamp);
|
||||
if (!err)
|
||||
return err;
|
||||
thread->comm_set = true;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define __PERF_THREAD_H
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/list.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include "symbol.h"
|
||||
@ -18,13 +19,14 @@ struct thread {
|
||||
char shortname[3];
|
||||
bool comm_set;
|
||||
bool dead; /* if set thread has exited */
|
||||
char *comm;
|
||||
struct list_head comm_list;
|
||||
int comm_len;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct machine;
|
||||
struct comm;
|
||||
|
||||
struct thread *thread__new(pid_t pid, pid_t tid);
|
||||
void thread__delete(struct thread *self);
|
||||
@ -33,10 +35,12 @@ static inline void thread__exited(struct thread *thread)
|
||||
thread->dead = true;
|
||||
}
|
||||
|
||||
int thread__set_comm(struct thread *self, const char *comm);
|
||||
int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp);
|
||||
int thread__comm_len(struct thread *self);
|
||||
struct comm *thread__comm(const struct thread *thread);
|
||||
const char *thread__comm_str(const struct thread *thread);
|
||||
void thread__insert_map(struct thread *self, struct map *map);
|
||||
int thread__fork(struct thread *self, struct thread *parent);
|
||||
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp);
|
||||
size_t thread__fprintf(struct thread *thread, FILE *fp);
|
||||
|
||||
static inline struct map *thread__find_map(struct thread *self,
|
||||
|
Loading…
x
Reference in New Issue
Block a user