perf tools: Speed-up perf report by using multi thread (v2)
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 22/38] perf callchain: Use session__find_addr_location() and friends
In reply to Namhyung KimEm Tue, Mar 03, 2015 at 12:07:34PM +0900, Namhyung Kim escreveu:
Quoted 2 lines
Pass session struct to callchain resolve routines and find correct thread/map/symbol using proper functions.
For instance, in this case it looks like a case to pass a 'struct machines' and not a perf_session one.
- Arnaldo
Quoted 571 lines
Cc Frederic Weisbecker <[email protected]>Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/builtin-script.c | 4 +-- tools/perf/tests/dwarf-unwind.c | 2 +- tools/perf/util/callchain.c | 6 ++-- tools/perf/util/callchain.h | 4 ++- tools/perf/util/hist.c | 5 +-- tools/perf/util/machine.c | 41 +++++++++++++--------- tools/perf/util/machine.h | 1 + .../util/scripting-engines/trace-event-python.c | 27 +++++++------- tools/perf/util/session.c | 6 ++-- tools/perf/util/session.h | 2 +- tools/perf/util/unwind-libdw.c | 14 ++++---- tools/perf/util/unwind-libdw.h | 1 + tools/perf/util/unwind-libunwind.c | 32 +++++++++-------- tools/perf/util/unwind.h | 3 +- 14 files changed, 86 insertions(+), 62 deletions(-)tools/perf/builtin-script.c +2
-2@@ -429,7 +429,7 @@ static void print_sample_bts(union perf_event *event, print_opts &= ~PRINT_IP_OPT_SRCLINE ; } } -perf_evsel__print_ip (evsel, sample, al, print_opts, +perf_evsel__print_ip (evsel, sample, session, al, print_opts,PERF_MAX_STACK_DEPTH ); }@@ -483,7 +483,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample,else printf ("\n" ); -perf_evsel__print_ip (evsel, sample, al, +perf_evsel__print_ip (evsel, sample, session, al, output[attr->type].print_ip_opts,PERF_MAX_STACK_DEPTH ); }tools/perf/tests/dwarf-unwind.c +1
-1@@ -75,7 +75,7 @@ static int unwind_thread(struct thread *thread) goto out; } - err =unwind__get_entries (unwind_entry, &cnt, thread, + err =unwind__get_entries (unwind_entry, &cnt, thread,NULL , &sample,MAX_STACK );if (err)pr_debug ("unwind failed\n" );tools/perf/util/callchain.c +4
-2@@ -757,7 +757,9 @@ int callchain_cursor_append(struct callchain_cursor *cursor,return 0 ; } -int sample__resolve_callchain (struct perf_sample *sample,struct symbol **parent, +int sample__resolve_callchain (struct perf_sample *sample, +struct perf_session *session, +struct symbol **parent,struct perf_evsel *evsel,struct addr_location *al,int max_stack) {@@ -767,7 +769,7 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parentif (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || sort__has_parent) {return thread__resolve_callchain (al->thread, evsel, sample, - parent, al, max_stack); + session, parent, al, max_stack); }return 0 ; }tools/perf/util/callchain.h +3
-1@@ -165,7 +165,9 @@ struct hist_entry;int record_parse_callchain_opt (const struct option *opt,const char *arg,int unset);int record_callchain_opt (const struct option *opt,const char *arg,int unset); -int sample__resolve_callchain (struct perf_sample *sample,struct symbol **parent, +int sample__resolve_callchain (struct perf_sample *sample, +struct perf_session *session, +struct symbol **parent,struct perf_evsel *evsel,struct addr_location *al,int max_stack);int hist_entry__append_callchain (struct hist_entry *he,struct perf_sample *sample);tools/perf/util/hist.c +3
-2@@ -861,8 +861,9 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, {int err, err2; - err =sample__resolve_callchain (iter->sample, &iter->parent, - iter->evsel, al, max_stack_depth); + err =sample__resolve_callchain (iter->sample, iter->session, + &iter->parent, iter->evsel, al, + max_stack_depth);if (err)return err;tools/perf/util/machine.c +25
-16@@ -1540,10 +1540,11 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, }static int add_callchain_ip (struct thread *thread, +struct perf_session *session,struct symbol **parent,struct addr_location *root_al,bool branch_history, -u64 ip) +u64 ip,u64 timestamp) {struct addr_location al;@@ -1551,8 +1552,9 @@ static int add_callchain_ip(struct thread *thread, al.sym =NULL ;if (branch_history) -thread__find_cpumode_addr_location (thread,MAP__FUNCTION , - ip, &al); +session__find_cpumode_addr_location (session, thread, +MAP__FUNCTION , ip, &al, + timestamp);else {u8 cpumode =PERF_RECORD_MISC_USER ;@@ -1579,8 +1581,8 @@ static int add_callchain_ip(struct thread *thread, }return 0 ; } -thread__find_addr_location (thread, cpumode,MAP__FUNCTION , - ip, &al); +session__find_addr_location (session,thread, cpumode, +MAP__FUNCTION , ip, &al, timestamp); }if (al.sym !=NULL ) {@@ -1670,6 +1672,7 @@ static int remove_loops(struct branch_entry *l, int nr)*/ static int resolve_lbr_callchain_sample (struct thread *thread,struct perf_sample *sample, +struct perf_session *session,struct symbol **parent,struct addr_location *root_al,int max_stack)@@ -1722,7 +1725,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread, ip = lbr_stack->entries[0 ].to; } - err =add_callchain_ip (thread, parent, root_al, false, ip); + err =add_callchain_ip (thread, session, parent, root_al, + false, ip, sample->time);if (err)return (err <0 ) ? err :0 ; }@@ -1735,6 +1739,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,static int thread__resolve_callchain_sample (struct thread *thread,struct perf_evsel *evsel,struct perf_sample *sample, +struct perf_session *session,struct symbol **parent,struct addr_location *root_al,int max_stack)@@ -1742,6 +1747,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,struct branch_stack *branch = sample->branch_stack;struct ip_callchain *chain = sample->callchain;int chain_nr =min (max_stack, (int )chain->nr); +u64 timestamp = sample->time;int i, j, err;int skip_idx =-1 ;int first_call =0 ;@@ -1749,8 +1755,8 @@ static int thread__resolve_callchain_sample(struct thread *thread,callchain_cursor_reset (&callchain_cursor);if (has_branch_callstack (evsel)) { - err =resolve_lbr_callchain_sample (thread, sample, parent, - root_al, max_stack); + err =resolve_lbr_callchain_sample (thread, sample, session, + parent, root_al, max_stack);if (err)return (err <0 ) ? err :0 ; }@@ -1806,11 +1812,12 @@ static int thread__resolve_callchain_sample(struct thread *thread, nr =remove_loops (be, nr);for (i =0 ; i < nr; i++) { - err =add_callchain_ip (thread, parent, root_al, - true, be[i].to); + err =add_callchain_ip (thread, session, parent, root_al, + true, be[i].to, timestamp);if (!err) - err =add_callchain_ip (thread, parent, root_al, - true, be[i].from); + err =add_callchain_ip (thread, session, parent, + root_al, true, + be[i].from, timestamp);if (err == -EINVAL )break ;if (err)@@ -1839,7 +1846,8 @@ static int thread__resolve_callchain_sample(struct thread *thread,#endif ip = chain->ips[j]; - err =add_callchain_ip (thread, parent, root_al, false, ip); + err =add_callchain_ip (thread, session, parent, root_al, false, + ip, timestamp);if (err)return (err <0 ) ? err :0 ; }@@ -1857,12 +1865,13 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)int thread__resolve_callchain (struct thread *thread,struct perf_evsel *evsel,struct perf_sample *sample, +struct perf_session *session,struct symbol **parent,struct addr_location *root_al,int max_stack) { -int ret =thread__resolve_callchain_sample (thread, evsel, - sample, parent, +int ret =thread__resolve_callchain_sample (thread, evsel, sample, + session, parent, root_al, max_stack);if (ret)return ret;@@ -1878,7 +1887,7 @@ int thread__resolve_callchain(struct thread *thread,return 0 ;return unwind__get_entries (unwind_entry, &callchain_cursor, - thread, sample, max_stack); + thread, session, sample, max_stack); }tools/perf/util/machine.h +1
-0@@ -129,6 +129,7 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,int thread__resolve_callchain (struct thread *thread,struct perf_evsel *evsel,struct perf_sample *sample, +struct perf_session *session,struct symbol **parent,struct addr_location *root_al,int max_stack);tools/perf/util/scripting-engines/trace-event-python.c +15
-12@@ -298,9 +298,10 @@ static PyObject *get_field_numeric_entry(struct event_format *event, } -static PyObject *python_process_callchain (struct perf_sample *sample, -struct perf_evsel *evsel, -struct addr_location *al) +static PyObject *python_process_callchain (struct perf_session *session, +struct perf_sample *sample, +struct perf_evsel *evsel, +struct addr_location *al) {PyObject *pylist;@@ -311,9 +312,8 @@ static PyObject *python_process_callchain(struct perf_sample *sample,if (!symbol_conf.use_callchain || !sample->callchain) goto exit; -if (thread__resolve_callchain (al->thread, evsel, - sample,NULL ,NULL , -PERF_MAX_STACK_DEPTH ) !=0 ) { +if (thread__resolve_callchain (al->thread, evsel, sample, session, +NULL ,NULL ,PERF_MAX_STACK_DEPTH ) !=0 ) {pr_err ("Failed to resolve callchain. Skipping\n" ); goto exit; }@@ -374,7 +374,8 @@ static PyObject *python_process_callchain(struct perf_sample *sample, } -static void python_process_tracepoint (struct perf_sample *sample, +static void python_process_tracepoint (struct perf_session *session, +struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread,struct addr_location *al)@@ -424,7 +425,7 @@ static void python_process_tracepoint(struct perf_sample *sample,PyTuple_SetItem (t, n++, context);/* ip unwinding */ - callchain =python_process_callchain (sample, evsel, al); + callchain =python_process_callchain (session, sample, evsel, al);if (handler) {PyTuple_SetItem (t, n++,PyInt_FromLong (cpu));@@ -759,7 +760,8 @@ static int python_process_call_return(struct call_return *cr, void *data)return db_export__call_return (dbe, cr); } -static void python_process_general_event (struct perf_sample *sample, +static void python_process_general_event (struct perf_session *session, +struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread,struct addr_location *al)@@ -822,7 +824,7 @@ static void python_process_general_event(struct perf_sample *sample, }/* ip unwinding */ - callchain =python_process_callchain (sample, evsel, al); + callchain =python_process_callchain (session, sample, evsel, al);pydict_set_item_string_decref (dict,"callchain" , callchain);PyTuple_SetItem (t, n++, dict);@@ -846,7 +848,7 @@ static void python_process_event(union perf_event *event,switch (evsel->attr.type) {case PERF_TYPE_TRACEPOINT : -python_process_tracepoint (sample, evsel, thread, al); +python_process_tracepoint (session, sample, evsel, thread, al);break ;/* Reserve for future process_hw/sw/raw APIs */ default :@@ -854,7 +856,8 @@ static void python_process_event(union perf_event *event,db_export__sample (&tables->dbe, event, sample, evsel, thread, al, session); else -python_process_general_event (sample ,evsel ,thread ,al ); +python_process_general_event (session ,sample ,evsel , +thread ,al ); } }tools/perf/util/session.c +3
-3@@ -1550,7 +1550,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, }void perf_evsel__print_ip (struct perf_evsel *evsel,struct perf_sample *sample, -struct addr_location *al, +struct perf_session *session,struct addr_location *al,unsigned int print_opts,unsigned int stack_depth) {struct callchain_cursor_node *node;@@ -1565,8 +1565,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,if (symbol_conf.use_callchain && sample->callchain) {struct addr_location node_al; -if (thread__resolve_callchain (al->thread, evsel, - sample,NULL ,NULL , +if (thread__resolve_callchain (al->thread, evsel, sample, + session,NULL ,NULL ,PERF_MAX_STACK_DEPTH ) !=0 ) {if (verbose)error ("Failed to resolve callchain. Skipping\n" );tools/perf/util/session.h +1
-1@@ -102,7 +102,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, unsigned int type);void perf_evsel__print_ip (struct perf_evsel *evsel,struct perf_sample *sample, -struct addr_location *al, +struct perf_session *session,struct addr_location *al,unsigned int print_opts,unsigned int stack_depth); int perf_session__cpu_bitmap(struct perf_session *session,tools/perf/util/unwind-libdw.c +8
-6@@ -8,6 +8,7 @@#include "unwind-libdw.h" #include "machine.h" #include "thread.h" +#include "session.h" #include <linux/types.h> #include "event.h" #include "perf_regs.h" @@ -26,10 +27,9 @@ static int __report_module(struct addr_location *al, u64 ip,Dwfl_Module *mod;struct dso *dso =NULL ; -thread__find_addr_location (ui->thread, -PERF_RECORD_MISC_USER , -MAP__FUNCTION , ip, al); - +session__find_addr_location (ui->session, ui->thread, +PERF_RECORD_MISC_USER ,MAP__FUNCTION , + ip, al, ui->sample->time);if (al->map) dso = al->map->dso;@@ -89,8 +89,8 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,struct addr_location al;ssize_t size; -thread__find_addr_map (ui->thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , addr, &al); +session__find_addr_map (ui->session, ui->thread,PERF_RECORD_MISC_USER , +MAP__FUNCTION , addr, &al, ui->sample->time);if (!al.map) {pr_debug ("unwind: no map for %lx\n" , (unsigned long )addr);return -1 ;@@ -165,12 +165,14 @@ frame_callback(Dwfl_Frame *state, void *arg)int unwind__get_entries (unwind_entry_cb_t cb,void *arg,struct thread *thread, +struct perf_session *session,struct perf_sample *data,int max_stack) {struct unwind_info ui = { .sample = data, .thread = thread, + .session = session, .machine = thread->mg->machine, .cb = cb, .arg = arg,tools/perf/util/unwind-libdw.h +1
-0@@ -11,6 +11,7 @@ bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg);struct unwind_info {Dwfl *dwfl;struct perf_sample *sample; +struct perf_session *session;struct machine *machine;struct thread *thread;unwind_entry_cb_t cb;tools/perf/util/unwind-libunwind.c +17
-15@@ -86,6 +86,7 @@ UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,struct unwind_info {struct perf_sample *sample; +struct perf_session *session;struct machine *machine;struct thread *thread; };@@ -315,8 +316,8 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui) {struct addr_location al; -thread__find_addr_map (ui->thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , ip, &al); +session__find_addr_map (ui->session, ui->thread,PERF_RECORD_MISC_USER , +MAP__FUNCTION , ip, &al, ui->sample->time);return al.map; }@@ -406,20 +407,19 @@ get_proc_name(unw_addr_space_t __maybe_unused as,static int access_dso_mem (struct unwind_info *ui,unw_word_t addr,unw_word_t *data) { -struct addr_location al; +struct map *map;ssize_t size; -thread__find_addr_map (ui->thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , addr, &al); -if (!al.map) { + map =find_map (addr, ui); +if (!map) {pr_debug ("unwind: no map for %lx\n" , (unsigned long )addr);return -1 ; } -if (!al.map->dso) +if (!map->dso)return -1 ; - size =dso__data_read_addr (al.map->dso, al.map, ui->machine, + size =dso__data_read_addr (map->dso, map, ui->machine, addr, (u8 *) data,sizeof (*data));return !(size ==sizeof (*data));@@ -511,14 +511,14 @@ static void put_unwind_info(unw_addr_space_t __maybe_unused as,pr_debug ("unwind: put_unwind_info called\n" ); } -static int entry (u64 ip,struct thread *thread, -unwind_entry_cb_t cb,void *arg) +static int entry (u64 ip,struct thread *thread,struct perf_session *session, +u64 timestamp,unwind_entry_cb_t cb,void *arg) {struct unwind_entry e;struct addr_location al; -thread__find_addr_location (thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , ip, &al); +session__find_addr_location (session, thread,PERF_RECORD_MISC_USER , +MAP__FUNCTION , ip, &al, timestamp); e.ip = ip; e.map = al.map;@@ -620,20 +620,22 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,unw_word_t ip;unw_get_reg (&c,UNW_REG_IP , &ip); - ret = ip ?entry (ip, ui->thread, cb, arg) :0 ; + ret = ip ?entry (ip, ui->thread, ui->session, + ui->sample->time, cb, arg) :0 ; }return ret; }int unwind__get_entries (unwind_entry_cb_t cb,void *arg, -struct thread *thread, +struct thread *thread,struct perf_session *session,struct perf_sample *data,int max_stack) {u64 ip;struct unwind_info ui = { .sample = data, .thread = thread, + .session = session, .machine = thread->mg->machine, };int ret;@@ -645,7 +647,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,if (ret)return ret; - ret =entry (ip, thread, cb, arg); + ret =entry (ip, thread, session, data->time, cb, arg);if (ret)return -ENOMEM ;tools/perf/util/unwind.h +2
-1@@ -16,7 +16,7 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);#ifdef HAVE_DWARF_UNWIND_SUPPORT int unwind__get_entries (unwind_entry_cb_t cb,void *arg, -struct thread *thread, +struct thread *thread,struct perf_session *session,struct perf_sample *data,int max_stack);/* libunwind specific */ #ifdef HAVE_LIBUNWIND_SUPPORT @@ -38,6 +38,7 @@ static inline int unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __maybe_unused, struct thread *thread __maybe_unused, + struct perf_session *session __maybe_unused, struct perf_sample *data __maybe_unused, int max_stack __maybe_unused) {Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 23/38 perf tools: Add a test case for timed map groups handling
In reply to Namhyung KimA test case for verifying thread->mg and ->mg_list handling during time change and new thread__find_addr_map_time() and friends.
--- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 ++ tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-mg-time.c | 88 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 tools/perf/tests/thread-mg-time.c
tools/perf/tests/Build +1 -0
@@ -27,6 +27,7 @@ perf-y += mmap-thread-lookup.o perf-y += thread-comm.o perf-y += thread-mg-share.o perf-y += thread-lookup-time.o +perf-y += thread-mg-time.o perf-y += switch-tracking.o perf-y += keep-tracking.o perf-y += code-reading.o
tools/perf/tests/builtin-test.c +4 -0
@@ -175,6 +175,10 @@ static struct test { .func = test__thread_lookup_time, }, { + .desc = "Test thread map group handling with time", + .func = test__thread_mg_time, + }, + { .func = NULL, }, };
tools/perf/tests/tests.h +1 -0
@@ -53,6 +53,7 @@ int test__fdarray__filter(void);int test__fdarray__add (void );int test__thread_comm (void );int test__thread_lookup_time (void ); +int test__thread_mg_time (void );#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)#ifdef HAVE_DWARF_UNWIND_SUPPORT
tools/perf/tests/thread-mg-time.c new file +88 -0
88 lines not on this page · raw message
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 24/38 perf tools: Protect dso symbol loading using a mutex
In reply to Namhyung KimWhen multi-thread support for perf report is enabled, it's possible to access a dso concurrently. Add a new pthread_mutex to protect it from concurrent dso__load().
--- tools/perf/util/dso.c | 2 ++ tools/perf/util/dso.h | 1 + tools/perf/util/symbol.c | 34 ++++++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 10 deletions(-)
tools/perf/util/dso.c +2 -0
@@ -884,6 +884,7 @@ struct dso *dso__new(const char *name)RB_CLEAR_NODE (&dso->rb_node);INIT_LIST_HEAD (&dso->node);INIT_LIST_HEAD (&dso->data.open_entry); +pthread_mutex_init (&dso->lock,NULL ); }return dso;
@@ -913,6 +914,7 @@ void dso__delete(struct dso *dso)dso_cache__free (&dso->data.cache);dso__free_a2l (dso);zfree (&dso->symsrc_filename); +pthread_mutex_destroy (&dso->lock);free (dso); }
tools/perf/util/dso.h +1 -0
@@ -102,6 +102,7 @@ struct dsos { };struct dso { +pthread_mutex_t lock;struct list_head node;struct rb_node rb_node;/* rbtree node sorted by long name */ struct rb_root symbols[MAP__NR_TYPES ];
tools/perf/util/symbol.c +24 -10
@@ -1357,12 +1357,22 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)struct symsrc *syms_ss =NULL , *runtime_ss =NULL ;bool kmod; -dso__set_loaded (dso, map->type); +pthread_mutex_lock (&dso->lock); + +/* check again under the dso->lock */ +if (dso__loaded (dso, map->type)) { + ret =1 ; + goto out; + } + +if (dso->kernel) { +if (dso->kernel ==DSO_TYPE_KERNEL ) + ret =dso__load_kernel_sym (dso, map, filter); +else if (dso->kernel ==DSO_TYPE_GUEST_KERNEL ) + ret =dso__load_guest_kernel_sym (dso, map, filter); -if (dso->kernel ==DSO_TYPE_KERNEL ) -return dso__load_kernel_sym (dso, map, filter); -else if (dso->kernel ==DSO_TYPE_GUEST_KERNEL ) -return dso__load_guest_kernel_sym (dso, map, filter); + goto out; + }if (map->groups && map->groups->machine) machine = map->groups->machine;
@@ -1375,18 +1385,18 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)struct stat st;if (lstat (dso->name, &st) <0 ) -return -1 ; + goto out;if (st.st_uid && (st.st_uid !=geteuid ())) {pr_warning ("File %s not owned by current user or root, " "ignoring it.\n" , dso->name); -return -1 ; + goto out; } ret =dso__load_perf_map (dso, map, filter); dso->symtab_type = ret >0 ?DSO_BINARY_TYPE__JAVA_JIT :DSO_BINARY_TYPE__NOT_FOUND ; -return ret; + goto out; }if (machine)
@@ -1394,7 +1404,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) name =malloc (PATH_MAX );if (!name) -return -1 ; + goto out; kmod = dso->symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
@@ -1475,7 +1485,11 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) out_free:free (name);if (ret <0 &&strstr (dso->name," (deleted)" ) !=NULL ) -return 0 ; + ret =0 ; +out: +dso__set_loaded (dso, map->type); +pthread_mutex_unlock (&dso->lock); +return ret; }
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 25/38 perf tools: Protect dso cache tree using dso->lock
In reply to Namhyung KimThe dso cache is accessed during dwarf callchain unwind and it might be processed concurrently when multi-thread report is enabled. Protect it under dso->lock.
Note that it doesn't protect dso_cache__find(). I think it's safe to access to the cache tree without the lock since we don't delete nodes. It it missed an existing node due to rotation, it'll find it during dso_cache__insert() anyway.
--- tools/perf/util/dso.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-)
tools/perf/util/dso.c +27 -7
@@ -443,10 +443,12 @@ bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by) }static void -dso_cache__free (struct rb_root *root) +dso_cache__free (struct dso *dso) { +struct rb_root *root = &dso->data.cache;struct rb_node *next =rb_first (root); +pthread_mutex_lock (&dso->lock);while (next) {struct dso_cache *cache;
@@ -455,10 +457,12 @@ dso_cache__free(struct rb_root *root)rb_erase (&cache->rb_node, root);free (cache); } +pthread_mutex_unlock (&dso->lock); } -static struct dso_cache *dso_cache__find (const struct rb_root *root,u64 offset) +static struct dso_cache *dso_cache__find (struct dso *dso,u64 offset) { +const struct rb_root *root = &dso->data.cache;struct rb_node *const *p = &root->rb_node;const struct rb_node *parent =NULL ;struct dso_cache *cache;
@@ -477,17 +481,20 @@ static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)else return cache; } +return NULL ; } -static void -dso_cache__insert (struct rb_root *root,struct dso_cache *new) +static struct dso_cache * +dso_cache__insert (struct dso *dso,struct dso_cache *new) { +struct rb_root *root = &dso->data.cache;struct rb_node **p = &root->rb_node;struct rb_node *parent =NULL ;struct dso_cache *cache;u64 offset = new->offset; +pthread_mutex_lock (&dso->lock);while (*p !=NULL ) {u64 end;
@@ -499,10 +506,17 @@ dso_cache__insert(struct rb_root *root, struct dso_cache *new) p = &(*p)->rb_left; else if (offset >= end) p = &(*p)->rb_right; + else + goto out; }rb_link_node (&new->rb_node, parent, p);rb_insert_color (&new->rb_node, root); + + cache =NULL ; +out: +pthread_mutex_unlock (&dso->lock); +return cache; } static ssize_t
@@ -520,6 +534,7 @@ static ssize_t dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) { struct dso_cache *cache; +struct dso_cache *old;ssize_t ret; do {
@@ -539,7 +554,12 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) cache->offset = cache_offset; cache->size = ret; -dso_cache__insert (&dso->data.cache, cache); + old =dso_cache__insert (dso, cache); +if (old) { +/* we lose the race */ +free (cache); + cache = old; + } ret =dso_cache__memcpy (cache, offset, data, size);
@@ -556,7 +576,7 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset, {struct dso_cache *cache; - cache =dso_cache__find (&dso->data.cache, offset); + cache =dso_cache__find (dso, offset);if (cache)return dso_cache__memcpy (cache, offset, data, size);else
@@ -911,7 +931,7 @@ void dso__delete(struct dso *dso) }dso__data_close (dso); -dso_cache__free (&dso->data.cache); +dso_cache__free (dso);dso__free_a2l (dso);zfree (&dso->symsrc_filename);pthread_mutex_destroy (&dso->lock);
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 26/38 perf tools: Protect dso cache fd with a mutex
In reply to Namhyung KimWhen dso cache is accessed in multi-thread environment, it's possible to close other dso->data.fd during operation due to open file limit. Protect the file descriptors using a separate mutex.
--- tools/perf/util/dso.c | 98 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 26 deletions(-)
tools/perf/util/dso.c +72 -26
@@ -213,6 +213,7 @@ bool dso__needs_decompress(struct dso *dso)*/ static LIST_HEAD (dso__data_open);static long dso__data_open_cnt; +static pthread_mutex_t dso__data_open_lock =PTHREAD_MUTEX_INITIALIZER ;static void dso__list_add (struct dso *dso) {
@@ -382,7 +383,9 @@ static void check_data_close(void)*/ void dso__data_close (struct dso *dso) { +pthread_mutex_lock (&dso__data_open_lock);close_dso (dso); +pthread_mutex_unlock (&dso__data_open_lock); }/**
@@ -405,6 +408,8 @@ int dso__data_fd(struct dso *dso, struct machine *machine)if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; +pthread_mutex_lock (&dso__data_open_lock); +if (dso->data.fd >=0 ) goto out;
@@ -427,6 +432,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine)else dso->data.status =DSO_DATA_STATUS_ERROR ; +pthread_mutex_unlock (&dso__data_open_lock);return dso->data.fd; }
@@ -531,7 +537,8 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset, }static ssize_t -dso_cache__read (struct dso *dso,u64 offset,u8 *data,ssize_t size) +dso_cache__read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {struct dso_cache *cache;struct dso_cache *old;
@@ -540,11 +547,24 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) do { u64 cache_offset; - ret = -ENOMEM; - cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); if (!cache) - break; + return -ENOMEM; + + pthread_mutex_lock(&dso__data_open_lock); + + /* + * dso->data.fd might be closed if other thread opened another + * file (dso) due to open file limit (RLIMIT_NOFILE). + */ + if (dso->data.fd < 0) { + dso->data.fd = open_dso(dso, machine); + if (dso->data.fd < 0) { + ret = -errno; + dso->data.status = DSO_DATA_STATUS_ERROR; + break; + } + } cache_offset = offset & DSO__DATA_CACHE_MASK;
@@ -554,6 +574,11 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) cache->offset = cache_offset; cache->size = ret; + }while (0 ); + +pthread_mutex_unlock (&dso__data_open_lock); + +if (ret >0 ) { old =dso_cache__insert (dso, cache);if (old) {/* we lose the race */
@@ -562,8 +587,7 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) } ret =dso_cache__memcpy (cache, offset, data, size); - - }while (0 ); + }if (ret <=0 )free (cache);
@@ -571,8 +595,8 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)return ret; } -static ssize_t dso_cache_read (struct dso *dso,u64 offset, -u8 *data,ssize_t size) +static ssize_t dso_cache_read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {struct dso_cache *cache;
@@ -580,7 +604,7 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,if (cache)return dso_cache__memcpy (cache, offset, data, size);else -return dso_cache__read (dso, offset, data, size); +return dso_cache__read (dso, machine, offset, data, size); }/*
@@ -588,7 +612,8 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,* in the rb_tree. Any read to already cached data is served * by cached data. */ -static ssize_t cached_read (struct dso *dso,u64 offset,u8 *data,ssize_t size) +static ssize_t cached_read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {ssize_t r =0 ;u8 *p = data;
@@ -596,7 +621,7 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) do { ssize_t ret; - ret = dso_cache_read(dso, offset, p, size); + ret = dso_cache_read(dso, machine, offset, p, size); if (ret < 0) return ret;
@@ -616,21 +641,42 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)return r; } -static int data_file_size (struct dso *dso) +static int data_file_size (struct dso *dso,struct machine *machine) { +int ret =0 ;struct stat st;char sbuf[STRERR_BUFSIZE ]; -if (!dso->data.file_size) { -if (fstat (dso->data.fd, &st)) { -pr_err ("dso mmap failed, fstat: %s\n" , -strerror_r (errno, sbuf,sizeof (sbuf))); -return -1 ; +if (dso->data.file_size) +return 0 ; + +pthread_mutex_lock (&dso__data_open_lock); + +/* +* dso->data.fd might be closed if other thread opened another +* file (dso) due to open file limit (RLIMIT_NOFILE). +*/ +if (dso->data.fd <0 ) { + dso->data.fd =open_dso (dso, machine); +if (dso->data.fd <0 ) { + ret = -errno; + dso->data.status =DSO_DATA_STATUS_ERROR ; + goto out; } - dso->data.file_size = st.st_size; } -return 0 ; +if (fstat (dso->data.fd, &st) <0 ) { + ret = -errno; +pr_err ("dso cache fstat failed: %s\n" , +strerror_r (errno, sbuf,sizeof (sbuf))); + dso->data.status =DSO_DATA_STATUS_ERROR ; + goto out; + } + dso->data.file_size = st.st_size; + +out: +pthread_mutex_unlock (&dso__data_open_lock); +return ret; }/**
@@ -648,17 +694,17 @@ off_t dso__data_size(struct dso *dso, struct machine *machine)if (fd <0 )return fd; -if (data_file_size (dso)) +if (data_file_size (dso, machine))return -1 ;/* For now just estimate dso data size is close to file size */ return dso->data.file_size; } -static ssize_t data_read_offset (struct dso *dso,u64 offset, -u8 *data,ssize_t size) +static ssize_t data_read_offset (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) { -if (data_file_size (dso)) +if (data_file_size (dso, machine))return -1 ;/* Check the offset sanity. */
@@ -668,7 +714,7 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,if (offset + size < offset)return -1 ; -return cached_read (dso, offset, data, size); +return cached_read (dso, machine, offset, data, size); }/**
@@ -685,10 +731,10 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,ssize_t dso__data_read_offset (struct dso *dso,struct machine *machine,u64 offset,u8 *data,ssize_t size) { -if (dso__data_fd (dso, machine) <0 ) +if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; -return data_read_offset (dso, offset, data, size); +return data_read_offset (dso, machine, offset, data, size); }/**
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 27/38 perf callchain: Maintain libunwind's address space in map_groups
In reply to Namhyung KimCurrently the address_space was kept in thread struct but it's more appropriate to keep it in map_groups as it's maintained with time. Also we don't need to flush after exec since it still can be accessed when used with an indexed data file.
--- tools/perf/tests/dwarf-unwind.c | 4 ++-- tools/perf/util/map.c | 5 +++++ tools/perf/util/map.h | 1 + tools/perf/util/thread.c | 8 -------- tools/perf/util/unwind-libunwind.c | 28 +++++++++++++--------------- tools/perf/util/unwind.h | 15 ++++++--------- 6 files changed, 27 insertions(+), 34 deletions(-)
tools/perf/tests/dwarf-unwind.c +2 -2
@@ -143,6 +143,8 @@ int test__dwarf_unwind(void)struct thread *thread;int err =-1 ; + callchain_param.record_mode =CALLCHAIN_DWARF ; +machines__init (&machines); machine =machines__find (&machines,HOST_KERNEL_ID );
@@ -151,8 +153,6 @@ int test__dwarf_unwind(void)return -1 ; } - callchain_param.record_mode =CALLCHAIN_DWARF ; -if (init_live_machine (machine)) {pr_err ("Could not init machine\n" ); goto out;
tools/perf/util/map.c +5 -0
@@ -14,6 +14,7 @@#include "util.h" #include "debug.h" #include "machine.h" +#include "unwind.h" #include <linux/string.h> const char *map_type__name[MAP__NR_TYPES ] = {
@@ -424,6 +425,8 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) mg->refcnt =1 ; mg->timestamp =0 ;INIT_LIST_HEAD (&mg->list); + +unwind__prepare_access (mg); }static void maps__delete (struct rb_root *maps)
@@ -457,6 +460,8 @@ void map_groups__exit(struct map_groups *mg)maps__delete (&mg->maps[i]);maps__delete_removed (&mg->removed_maps[i]); } + +unwind__finish_access (mg); }bool map_groups__empty (struct map_groups *mg)
tools/perf/util/map.h +1 -0
@@ -64,6 +64,7 @@ struct map_groups {u64 timestamp;int refcnt;struct list_head list; +void *priv; };struct map_groups *map_groups__new (struct machine *machine);
tools/perf/util/thread.c +0 -8
@@ -8,7 +8,6 @@#include "util.h" #include "debug.h" #include "comm.h" -#include "unwind.h" struct map_groups *thread__get_map_groups (struct thread *thread,u64 timestamp) {
@@ -104,9 +103,6 @@ struct thread *thread__new(pid_t pid, pid_t tid)INIT_LIST_HEAD (&thread->tid_node);INIT_LIST_HEAD (&thread->mg_list); -if (unwind__prepare_access (thread) <0 ) - goto err_thread; - comm_str =malloc (32 );if (!comm_str) goto err_thread;
@@ -147,7 +143,6 @@ void thread__delete(struct thread *thread)list_del (&comm->list);comm__free (comm); } -unwind__finish_access (thread);free (thread); }
@@ -216,9 +211,6 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,break ; }list_add_tail (&new->list, &curr->list); - -if (exec) -unwind__flush_access (thread); }if (exec) {
tools/perf/util/unwind-libunwind.c +13 -15
@@ -32,6 +32,7 @@#include "symbol.h" #include "util.h" #include "debug.h" +#include "map.h" extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
@@ -560,7 +561,7 @@ static unw_accessors_t accessors = { .get_proc_name = get_proc_name, }; -int unwind__prepare_access (struct thread *thread) +int unwind__prepare_access (struct map_groups *mg) {unw_addr_space_t addr_space;
@@ -574,41 +575,38 @@ int unwind__prepare_access(struct thread *thread) }unw_set_caching_policy (addr_space,UNW_CACHE_GLOBAL ); -thread__set_priv (thread, addr_space); + mg->priv = addr_space;return 0 ; } -void unwind__flush_access (struct thread *thread) +void unwind__finish_access (struct map_groups *mg) { -unw_addr_space_t addr_space; +unw_addr_space_t addr_space = mg->priv;if (callchain_param.record_mode !=CALLCHAIN_DWARF )return ; - addr_space =thread__priv (thread); -unw_flush_cache (addr_space,0 ,0 ); -} - -void unwind__finish_access (struct thread *thread) -{ -unw_addr_space_t addr_space; - -if (callchain_param.record_mode !=CALLCHAIN_DWARF ) +if (addr_space ==NULL )return ; - addr_space =thread__priv (thread);unw_destroy_addr_space (addr_space); + mg->priv =NULL ; }static int get_entries (struct unwind_info *ui,unwind_entry_cb_t cb,void *arg,int max_stack) { +struct map_groups *mg;unw_addr_space_t addr_space;unw_cursor_t c;int ret; - addr_space =thread__priv (ui->thread); + mg =thread__get_map_groups (ui->thread, ui->sample->time); +if (mg ==NULL ) +return -1 ; + + addr_space = mg->priv;if (addr_space ==NULL )return -1 ;
tools/perf/util/unwind.h +6 -9
@@ -21,17 +21,15 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,/* libunwind specific */ #ifdef HAVE_LIBUNWIND_SUPPORT int libunwind__arch_reg_id (int regnum); -int unwind__prepare_access (struct thread *thread); -void unwind__flush_access (struct thread *thread); -void unwind__finish_access (struct thread *thread); +int unwind__prepare_access (struct map_groups *mg); +void unwind__finish_access (struct map_groups *mg);#else -static inline int unwind__prepare_access (struct thread *thread__maybe_unused ) +static inline int unwind__prepare_access (struct map_groups *mg__maybe_unused ) {return 0 ; } -static inline void unwind__flush_access (struct thread *thread__maybe_unused ) {} -static inline void unwind__finish_access (struct thread *thread__maybe_unused ) {} +static inline void unwind__finish_access (struct map_groups *mg__maybe_unused ) {}#endif #else static inline int
@@ -45,12 +43,11 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,return 0 ; } -static inline int unwind__prepare_access (struct thread *thread__maybe_unused ) +static inline int unwind__prepare_access (struct map_groups *mg__maybe_unused ) {return 0 ; } -static inline void unwind__flush_access (struct thread *thread__maybe_unused ) {} -static inline void unwind__finish_access (struct thread *thread__maybe_unused ) {} +static inline void unwind__finish_access (struct map_groups *mg__maybe_unused ) {}#endif /* HAVE_DWARF_UNWIND_SUPPORT */ #endif /* __UNWIND_H */
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 28/38 perf tools: Add dso__data_get/put_fd()
In reply to Namhyung KimUsing dso__data_fd() in multi-thread environment is not safe since returned fd can be closed and/or reused anytime. So convert it to the dso__data_get/put_fd() pair to protect the access with lock.
The original dso__data_fd() is deprecated and kept only for testing.
--- tools/perf/util/dso.c | 44 +++++++++++++++++++++++++++++--------- tools/perf/util/dso.h | 9 ++++++-- tools/perf/util/unwind-libunwind.c | 38 +++++++++++++++++++------------- 3 files changed, 64 insertions(+), 27 deletions(-)
tools/perf/util/dso.c +34 -10
@@ -389,14 +389,15 @@ void dso__data_close(struct dso *dso) }/** -* dso__data_fd - Get dso's data file descriptor +* dso__data_get_fd - Get dso's data file descriptor * @dso: dso object * @machine: machine object * * External interface to find dso's file, open it and -* returns file descriptor. +* returns file descriptor. Should be paired with +* dso__data_put_fd(). */ -int dso__data_fd (struct dso *dso,struct machine *machine) +int dso__data_get_fd (struct dso *dso,struct machine *machine) {enum dso_binary_type binary_type_data[] = {DSO_BINARY_TYPE__BUILD_ID_CACHE ,
@@ -405,11 +406,11 @@ int dso__data_fd(struct dso *dso, struct machine *machine) };int i =0 ; +pthread_mutex_lock (&dso__data_open_lock); +if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; -pthread_mutex_lock (&dso__data_open_lock); -if (dso->data.fd >=0 ) goto out;
@@ -432,10 +433,31 @@ int dso__data_fd(struct dso *dso, struct machine *machine)else dso->data.status =DSO_DATA_STATUS_ERROR ; -pthread_mutex_unlock (&dso__data_open_lock);return dso->data.fd; } +void dso__data_put_fd (struct dso *dso__maybe_unused ) +{ +pthread_mutex_unlock (&dso__data_open_lock); +} + +/** +* dso__data_get_fd - Get dso's data file descriptor +* @dso: dso object +* @machine: machine object +* +* Obsolete interface to find dso's file, open it and +* returns file descriptor. It's not thread-safe in that +* the returned fd may be reused for other file. +*/ +int dso__data_fd (struct dso *dso,struct machine *machine) +{ +int fd =dso__data_get_fd (dso, machine); + +dso__data_put_fd (dso); +return fd; +} +bool dso__data_status_seen (struct dso *dso,enum dso_data_status_seen by) {u32 flag =1 << by;
@@ -1144,10 +1166,12 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)enum dso_type dso__type (struct dso *dso,struct machine *machine) {int fd; +enum dso_type type =DSO__TYPE_UNKNOWN ; - fd =dso__data_fd (dso, machine); -if (fd <0 ) -return DSO__TYPE_UNKNOWN ; + fd =dso__data_get_fd (dso, machine); +if (fd >=0 ) + type =dso__type_fd (fd); +dso__data_put_fd (dso); -return dso__type_fd (fd); +return type; }
tools/perf/util/dso.h +7 -2
@@ -197,7 +197,9 @@ bool dso__needs_decompress(struct dso *dso);/* * The dso__data_* external interface provides following functions: -* dso__data_fd +* dso__data_fd (obsolete) +* dso__data_get_fd +* dso__data_put_fd * dso__data_close * dso__data_size * dso__data_read_offset
@@ -214,8 +216,9 @@ bool dso__needs_decompress(struct dso *dso);* The current usage of the dso__data_* interface is as follows: * * Get DSO's fd: -* int fd = dso__data_fd(dso, machine); +* int fd = dso__data_get_fd(dso, machine); * USE 'fd' SOMEHOW +* dso__data_put_fd(dso) * * Read DSO's data: * n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE);
@@ -235,6 +238,8 @@ bool dso__needs_decompress(struct dso *dso);* TODO */ int dso__data_fd (struct dso *dso,struct machine *machine); +int dso__data_get_fd (struct dso *dso,struct machine *machine); +void dso__data_put_fd (struct dso *dso);void dso__data_close (struct dso *dso);off_t dso__data_size (struct dso *dso,struct machine *machine);
tools/perf/util/unwind-libunwind.c +23 -15
@@ -271,13 +271,13 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,u64 offset = dso->data.frame_offset;if (offset ==0 ) { - fd =dso__data_fd (dso, machine); -if (fd <0 ) -return -EINVAL ; - -/* Check the .eh_frame section for unwinding info */ - offset =elf_section_offset (fd,".eh_frame_hdr" ); - dso->data.frame_offset = offset; + fd =dso__data_get_fd (dso, machine); +if (fd >=0 ) { +/* Check the .eh_frame section for unwinding info */ + offset =elf_section_offset (fd,".eh_frame_hdr" ); + dso->data.frame_offset = offset; + } +dso__data_put_fd (dso); }if (offset)
@@ -296,13 +296,19 @@ static int read_unwind_spec_debug_frame(struct dso *dso,u64 ofs = dso->data.frame_offset;if (ofs ==0 ) { - fd =dso__data_fd (dso, machine); -if (fd <0 ) -return -EINVAL ; - -/* Check the .debug_frame section for unwinding info */ - ofs =elf_section_offset (fd,".debug_frame" ); - dso->data.frame_offset = ofs; +int ret =0 ; + + fd =dso__data_get_fd (dso, machine); +if (fd >=0 ) { +/* Check the .debug_frame section for unwinding info */ + ofs =elf_section_offset (fd,".debug_frame" ); + dso->data.frame_offset = ofs; + }else + ret = -EINVAL ; + +dso__data_put_fd (dso); +if (ret) +return ret; } *offset = ofs;
@@ -355,10 +361,12 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,#ifndef NO_LIBUNWIND_DEBUG_FRAME /* Check the .debug_frame section for unwinding info */ if (!read_unwind_spec_debug_frame (map->dso, ui->machine, &segbase)) { -int fd =dso__data_fd (map->dso, ui->machine); +int fd =dso__data_get_fd (map->dso, ui->machine);int is_exec =elf_is_exec (fd, map->dso->name);unw_word_t base = is_exec ?0 : map->start; +dso__data_put_fd (map->dso); +memset (&di,0 ,sizeof (di));if (dwarf_find_debug_frame (0 , &di, ip, base, map->dso->name, map->start, map->end))
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 29/38 perf session: Pass struct events stats to event processing functions
In reply to Namhyung KimPass stats structure so that it can point separate object when used in multi-thread environment.
--- tools/perf/util/ordered-events.c | 4 ++- tools/perf/util/session.c | 53 ++++++++++++++++++++++++---------------- tools/perf/util/session.h | 1 + 3 files changed, 36 insertions(+), 22 deletions(-)
tools/perf/util/ordered-events.c +3 -1
@@ -183,7 +183,9 @@ static int __ordered_events__flush(struct perf_session *s,if (ret)pr_err ("Can't parse sample, err = %d\n" , ret);else { - ret =perf_session__deliver_event (s, iter->event, &sample, tool, + ret =perf_session__deliver_event (s, &s->evlist->stats, + iter->event, + &sample, tool, iter->file_offset);if (ret)return ret;
tools/perf/util/session.c +32 -21
195 lines not on this page · raw message
tools/perf/util/session.h +1 -0
@@ -58,6 +58,7 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event,void perf_tool__fill_defaults (struct perf_tool *tool);int perf_session__deliver_event (struct perf_session *session, +struct events_stats *stats,union perf_event *event,struct perf_sample *sample,struct perf_tool *tool,u64 file_offset);
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 30/38 perf hists: Pass hists struct to hist_entry_iter struct
In reply to Namhyung KimThis is a preparation for perf report multi-thread support. When multi-thread is enable, each thread will have its own hists during the sample processing.
--- tools/perf/builtin-report.c | 1 + tools/perf/builtin-top.c | 1 + tools/perf/tests/hists_cumulate.c | 1 + tools/perf/tests/hists_filter.c | 1 + tools/perf/tests/hists_output.c | 1 + tools/perf/util/hist.c | 20 +++++++------------- tools/perf/util/hist.h | 1 + 7 files changed, 13 insertions(+), 13 deletions(-)
tools/perf/builtin-report.c +1 -0
@@ -138,6 +138,7 @@ static int process_sample_event(struct perf_tool *tool,struct addr_location al;struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = sample, .session = rep->session, .hide_unresolved = rep->hide_unresolved,
tools/perf/builtin-top.c +1 -0
@@ -775,6 +775,7 @@ static void perf_event__process_sample(struct perf_tool *tool,struct hists *hists =evsel__hists (evsel);struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = sample, .session = top->session, .add_entry_cb = hist_iter__top_callback,
tools/perf/tests/hists_cumulate.c +1 -0
@@ -88,6 +88,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) };struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = &sample, .hide_unresolved = false, };
tools/perf/tests/hists_filter.c +1 -0
@@ -64,6 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, };struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false,
tools/perf/tests/hists_output.c +1 -0
@@ -58,6 +58,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) };struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false,
tools/perf/util/hist.c +7 -13
@@ -510,7 +510,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *alu64 cost;struct mem_info *mi = iter->priv;struct perf_sample *sample = iter->sample; -struct hists *hists =evsel__hists (iter->evsel); +struct hists *hists = iter->hists;struct hist_entry *he;if (mi ==NULL )
@@ -540,8 +540,7 @@ static int iter_finish_mem_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { - struct perf_evsel *evsel = iter->evsel; -struct hists *hists =evsel__hists (evsel); + struct hists *hists = iter->hists;struct hist_entry *he = iter->he;int err = -EINVAL ;
@@ -613,8 +612,7 @@ static int iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) { struct branch_info *bi; -struct perf_evsel *evsel = iter->evsel; -struct hists *hists =evsel__hists (evsel); +struct hists *hists = iter->hists;struct hist_entry *he =NULL ;int i = iter->curr;int err =0 ;
@@ -661,11 +659,10 @@ iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,static int iter_add_single_normal_entry (struct hist_entry_iter *iter,struct addr_location *al) { -struct perf_evsel *evsel = iter->evsel;struct perf_sample *sample = iter->sample;struct hist_entry *he; - he =__hists__add_entry (evsel__hists (evsel), al, iter->parent,NULL ,NULL , + he =__hists__add_entry (iter->hists, al, iter->parent,NULL ,NULL , sample->period, sample->weight, sample->transaction, sample->time, true);if (he ==NULL )
@@ -680,7 +677,6 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) {struct hist_entry *he = iter->he; -struct perf_evsel *evsel = iter->evsel;struct perf_sample *sample = iter->sample;if (he ==NULL )
@@ -688,7 +684,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, iter->he =NULL ; -hists__inc_nr_samples (evsel__hists (evsel), he->filtered); +hists__inc_nr_samples (iter->hists, he->filtered);return hist_entry__append_callchain (he, sample); }
@@ -720,8 +716,7 @@ static int iter_add_single_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct perf_evsel *evsel = iter->evsel; -struct hists *hists =evsel__hists (evsel); + struct hists *hists = iter->hists;struct perf_sample *sample = iter->sample;struct hist_entry **he_cache = iter->priv;struct hist_entry *he;
@@ -766,7 +761,6 @@ static int iter_add_next_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct perf_evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample;struct hist_entry **he_cache = iter->priv;struct hist_entry *he;
@@ -800,7 +794,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, } } - he =__hists__add_entry (evsel__hists (evsel), al, iter->parent,NULL ,NULL , + he =__hists__add_entry (iter->hists, al, iter->parent,NULL ,NULL , sample->period, sample->weight, sample->transaction, sample->time, false);if (he ==NULL )
tools/perf/util/hist.h +1 -0
@@ -86,6 +86,7 @@ struct hist_entry_iter {bool hide_unresolved; +struct hists *hists;struct perf_evsel *evsel;struct perf_sample *sample;struct perf_session *session;
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 31/38 perf tools: Move BUILD_ID_SIZE definition to perf.h
In reply to Namhyung KimThe util/event.h includes util/build-id.h only for BUILD_ID_SIZE. This is a problem when I include util/event.h from util/tool.h which is also included by util/build-id.h since it now makes a circular dependency resulting in incomplete type error.
--- tools/perf/perf.h | 1 + tools/perf/util/build-id.h | 2 -- tools/perf/util/dso.h | 1 + tools/perf/util/event.h | 1 - 4 files changed, 2 insertions(+), 3 deletions(-)
tools/perf/perf.h +1 -0
@@ -30,6 +30,7 @@ static inline unsigned long long rdclock(void) }#define MAX_NR_CPUS 256 +#define BUILD_ID_SIZE 20extern const char *input_name;extern bool perf_host, perf_guest;
tools/perf/util/build-id.h +0 -2
@@ -1,8 +1,6 @@#ifndef PERF_BUILD_ID_H_ #define PERF_BUILD_ID_H_ 1 -#define BUILD_ID_SIZE 20 -#include "tool.h" #include "strlist.h" #include <linux/types.h>
tools/perf/util/dso.h +1 -0
@@ -7,6 +7,7 @@#include <linux/types.h> #include <linux/bitops.h> #include "map.h" +#include "perf.h" #include "build-id.h" enum dso_binary_type {
tools/perf/util/event.h +0 -1
@@ -6,7 +6,6 @@#include "../perf.h" #include "map.h" -#include "build-id.h" #include "perf_regs.h" struct mmap_event {
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 32/38 perf report: Parallelize perf report using multi-thread
In reply to Namhyung KimIntroduce perf_session__process_events_mt() to enable multi-thread sample processing. It allocates a struct perf_tool_mt and fills needed info in it.
The session and hists event stats are counted for each thread and summed after finishing the processing. Similarly hist entries are added to per-thread hists first and then move to the original hists using hists__mt_resort(). This function reuses hists__collapse_ resort() code so makes sort__need_collapse force to true and skips the collapsing function.
Note that most of preprocessing stage is already done by processing meta events in dummy tracking evsel first. We can find corresponding thread and map based on the sample time and symbol loading and dso cache access is protected by pthread mutex.
--- tools/perf/util/hist.c | 75 +++++++++++++++++++----- tools/perf/util/hist.h | 3 + tools/perf/util/session.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/session.h | 2 + tools/perf/util/tool.h | 12 ++++ 5 files changed, 221 insertions(+), 13 deletions(-)
tools/perf/util/hist.c +62 -13
@@ -945,7 +945,7 @@ void hist_entry__delete(struct hist_entry *he)* collapse the histogram */ -static bool hists__collapse_insert_entry (struct hists *hists__maybe_unused , +static bool hists__collapse_insert_entry (struct hists *hists,struct rb_root *root,struct hist_entry *he) {
@@ -982,6 +982,13 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, } hists->nr_entries++; +/* +* For multi-threaded report, he->hists points to a dummy +* hists in the struct perf_tool_mt. Please see +* perf_session__process_events_mt(). +*/ + he->hists = hists; +rb_link_node (&he->rb_node_in, parent, p);rb_insert_color (&he->rb_node_in, root);return true;
@@ -1009,19 +1016,12 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)hists__filter_entry_by_symbol (hists, he); } -void hists__collapse_resort (struct hists *hists,struct ui_progress *prog) +static void __hists__collapse_resort (struct hists *hists,struct rb_root *root, +struct ui_progress *prog) { -struct rb_root *root;struct rb_node *next;struct hist_entry *n; -if (!sort__need_collapse) -return ; - - hists->nr_entries =0 ; - - root =hists__get_rotate_entries_in (hists); - next =rb_first (root);while (next) {
@@ -1044,6 +1044,27 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) } } +void hists__collapse_resort (struct hists *hists,struct ui_progress *prog) +{ +struct rb_root *root; + +if (!sort__need_collapse) +return ; + + hists->nr_entries =0 ; + + root =hists__get_rotate_entries_in (hists); +__hists__collapse_resort (hists, root, prog); +} + +void hists__mt_resort (struct hists *dst,struct hists *src) +{ +struct rb_root *root = src->entries_in; + + sort__need_collapse =1 ; +__hists__collapse_resort (dst, root,NULL ); +} +static int hist_entry__sort (struct hist_entry *a,struct hist_entry *b) {struct perf_hpp_fmt *fmt;
@@ -1272,6 +1293,29 @@ void events_stats__inc(struct events_stats *stats, u32 type) ++stats->nr_events[type]; } +void events_stats__add (struct events_stats *dst,struct events_stats *src) +{ +int i; + +#define ADD (_field) dst->_field += src->_field + +ADD (total_period); +ADD (total_non_filtered_period); +ADD (total_lost); +ADD (total_invalid_chains); +ADD (nr_non_filtered_samples); +ADD (nr_lost_warned); +ADD (nr_unknown_events); +ADD (nr_invalid_chains); +ADD (nr_unknown_id); +ADD (nr_unprocessable_samples); + +for (i =0 ; i <PERF_RECORD_HEADER_MAX ; i++) +ADD (nr_events[i]); + +#undef ADD +} +void hists__inc_nr_events (struct hists *hists,u32 type) {events_stats__inc (&hists->stats, type);
@@ -1448,16 +1492,21 @@ int perf_hist_config(const char *var, const char *value)return 0 ; } -static int hists_evsel__init (struct perf_evsel *evsel) +void __hists__init (struct hists *hists) { -struct hists *hists =evsel__hists (evsel); -memset (hists,0 ,sizeof (*hists)); hists->entries_in_array[0 ] = hists->entries_in_array[1 ] =RB_ROOT ; hists->entries_in = &hists->entries_in_array[0 ]; hists->entries_collapsed =RB_ROOT ; hists->entries =RB_ROOT ;pthread_mutex_init (&hists->lock,NULL ); +} + +static int hists_evsel__init (struct perf_evsel *evsel) +{ +struct hists *hists =evsel__hists (evsel); + +__hists__init (hists);return 0 ; }
tools/perf/util/hist.h +3 -0
@@ -123,6 +123,7 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,void hist_entry__delete (struct hist_entry *he);void hists__output_resort (struct hists *hists,struct ui_progress *prog); +void hists__mt_resort (struct hists *dst,struct hists *src);void hists__collapse_resort (struct hists *hists,struct ui_progress *prog);void hists__decay_entries (struct hists *hists,bool zap_user,bool zap_kernel);
@@ -135,6 +136,7 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h);void hists__inc_nr_events (struct hists *hists,u32 type);void hists__inc_nr_samples (struct hists *hists,bool filtered);void events_stats__inc (struct events_stats *stats,u32 type); +void events_stats__add (struct events_stats *dst,struct events_stats *src);size_t events_stats__fprintf (struct events_stats *stats,FILE *fp); size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
@@ -178,6 +180,7 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel) }int hists__init (void ); +void __hists__init (struct hists *hists);struct perf_hpp {char *buf;
tools/perf/util/session.c +142 -0
148 lines not on this page · raw message
tools/perf/util/session.h +2 -0
@@ -50,6 +50,8 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset,int perf_session__process_events (struct perf_session *session,struct perf_tool *tool); +int perf_session__process_events_mt (struct perf_session *session, +struct perf_tool *tool,void *arg); int perf_session_queue_event(struct perf_session *s, union perf_event *event, struct perf_tool *tool, struct perf_sample *sample,
tools/perf/util/tool.h +12 -0
@@ -2,6 +2,7 @@#define __PERF_TOOL_H#include <stdbool.h> +#include "util/event.h" struct perf_session ;union perf_event ;
@@ -10,6 +11,7 @@ struct perf_evsel;struct perf_sample ;struct perf_tool ;struct machine ; +struct hists ; typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample,
@@ -45,4 +47,14 @@ struct perf_tool { bool ordering_requires_timestamps; }; +struct perf_tool_mt { +struct perf_tool tool; +struct events_stats stats; +struct hists *hists; +struct perf_session *session; +int idx; + +void *priv; +}; +#endif /* __PERF_TOOL_H */
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 33/38 perf tools: Add missing_threads rb tree
In reply to Namhyung KimSometimes it's possible to miss certain meta events like fork/exit and in this case it can fail to find such thread in the machine's rbtree. But adding a thread to the tree is dangerous since it's now executed in multi-thread environment otherwise it'll add an overhead in order to grab a lock for every search. So adds a separate missing_threads tree and protect it with a mutex. It's expected to be accessed only if a thread is not found in a normal tree.
--- tools/perf/tests/thread-lookup-time.c | 8 ++- tools/perf/util/build-id.c | 9 ++- tools/perf/util/machine.c | 129 +++++++++++++++++++++------------- tools/perf/util/machine.h | 2 + tools/perf/util/session.c | 8 +-- tools/perf/util/thread.h | 1 + 6 files changed, 101 insertions(+), 56 deletions(-)
tools/perf/tests/thread-lookup-time.c +7 -1
@@ -7,7 +7,9 @@static int thread__print_cb (struct thread *th,void *arg__maybe_unused ) {printf ("thread: %d, start time: %" PRIu64" %s\n" , - th->tid, th->start_time, th->dead ?"(dead)" :"" ); + th->tid, th->start_time, + th->dead ?"(dead)" : th->exited ?"(exited)" : + th->missing ?"(missing)" :"" );return 0 ; }
@@ -105,6 +107,8 @@ static int lookup_with_timestamp(struct machine *machine) machine__findnew_thread_time(machine, 0, 0, 70000) == t3);machine__delete_threads (machine); +machine__delete_dead_threads (machine); +machine__delete_missing_threads (machine);return 0 ; }
@@ -146,6 +150,8 @@ static int lookup_without_timestamp(struct machine *machine) machine__findnew_thread_time(machine, 0, 0, -1ULL) == t3);machine__delete_threads (machine); +machine__delete_dead_threads (machine); +machine__delete_missing_threads (machine);return 0 ; }
tools/perf/util/build-id.c +8 -1
@@ -60,7 +60,14 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, event->fork.ppid, event->fork.ptid);if (thread) { -rb_erase (&thread->rb_node, &machine->threads); +if (thread->dead) +rb_erase (&thread->rb_node, &machine->dead_threads); +else if (thread->missing) +rb_erase (&thread->rb_node, &machine->missing_threads); +else +rb_erase (&thread->rb_node, &machine->threads); + +list_del (&thread->tid_node); machine->last_match =NULL ;thread__delete (thread); }
tools/perf/util/machine.c +81 -48
185 lines not on this page · raw message
tools/perf/util/machine.h +2 -0
@@ -31,6 +31,7 @@ struct machine {char *root_dir;struct rb_root threads;struct rb_root dead_threads; +struct rb_root missing_threads;struct thread *last_match;struct vdso_info *vdso_info;struct dsos user_dsos;
@@ -116,6 +117,7 @@ void machines__set_comm_exec(struct machines *machines, bool comm_exec);struct machine *machine__new_host (void );int machine__init (struct machine *machine,const char *root_dir,pid_t pid);void machine__exit (struct machine *machine); +void machine__delete_missing_threads (struct machine *machine);void machine__delete_dead_threads (struct machine *machine);void machine__delete_threads (struct machine *machine);void machine__delete (struct machine *machine);
tools/perf/util/session.c +2 -6
@@ -138,14 +138,11 @@ struct perf_session *perf_session__new(struct perf_data_file *file,return NULL ; } -static void perf_session__delete_dead_threads (struct perf_session *session) -{ -machine__delete_dead_threads (&session->machines.host); -} -static void perf_session__delete_threads (struct perf_session *session) {machine__delete_threads (&session->machines.host); +machine__delete_dead_threads (&session->machines.host); +machine__delete_missing_threads (&session->machines.host); }static void perf_session_env__delete (struct perf_session_env *env)
@@ -167,7 +164,6 @@ static void perf_session_env__delete(struct perf_session_env *env)void perf_session__delete (struct perf_session *session) {perf_session__destroy_kernel_maps (session); -perf_session__delete_dead_threads (session);perf_session__delete_threads (session);perf_session_env__delete (&session->header.env);machines__exit (&session->machines);
tools/perf/util/thread.h +1 -0
@@ -23,6 +23,7 @@ struct thread {bool comm_set;bool exited;/* if set thread has exited */ bool dead;/* thread is in dead_threads list */ +bool missing;/* thread is in missing_threads list */ struct list_head comm_list;int comm_len;u64 db_id;
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 34/38 perf record: Synthesize COMM event for a command line workload
In reply to Namhyung KimWhen perf creates a new child to profile, the events are enabled on exec(). And in this case, it doesn't synthesize any event for the child since they'll be generated during exec(). But there's an window between the enabling and the event generation.
It used to be overcome since samples are only in kernel (so we always have the map) and the comm is overridden by a later COMM event. However it won't work anymore since those samples will go to a missing thread now but the COMM event will create a (current) thread. This leads to those early samples (like native_write_msr_safe) not having a comm but pid (like ':15328').
So it needs to synthesize COMM event for the child explicitly before enabling so that it can have a correct comm. But at this time, the comm will be "perf" since it's not exec-ed yet.
--- tools/perf/builtin-record.c | 18 +++++++++++++++++- tools/perf/util/event.c | 2 +- tools/perf/util/event.h | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-)
tools/perf/builtin-record.c +17 -1
@@ -605,8 +605,24 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)/* * Let the child rip */ -if (forks) +if (forks) { +union perf_event *comm_event; + + comm_event =malloc (sizeof (*comm_event) + machine->id_hdr_size); +if (comm_event ==NULL ) + goto out_child; + + err =perf_event__synthesize_comm (tool, comm_event, + rec->evlist->threads->map[0 ], + process_synthesized_event, + machine); +free (comm_event); + +if (err <0 ) + goto out_child; +perf_evlist__start_workload (rec->evlist); + }if (opts->initial_delay) {usleep (opts->initial_delay *1000 );
tools/perf/util/event.c +1 -1
@@ -127,7 +127,7 @@ static pid_t perf_event__prepare_comm(union perf_event *event, pid_t pid,return tgid; } -static pid_t perf_event__synthesize_comm (struct perf_tool *tool, +pid_t perf_event__synthesize_comm (struct perf_tool *tool,union perf_event *event,pid_t pid,perf_event__handler_t process,struct machine *machine)
tools/perf/util/event.h +5 -0
@@ -386,6 +386,11 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,struct machine *machine, bool mmap_data); +pid_t perf_event__synthesize_comm (struct perf_tool *tool, +union perf_event *event,pid_t pid, +perf_event__handler_t process, +struct machine *machine); +size_t perf_event__fprintf_comm (union perf_event *event,FILE *fp);size_t perf_event__fprintf_mmap (union perf_event *event,FILE *fp);size_t perf_event__fprintf_mmap2 (union perf_event *event,FILE *fp);
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 35/38 perf tools: Fix progress ui to support multi thread
In reply to Namhyung KimSplit ui_progress struct into global and local one. Each thread updates local struct without lock and only updates global one if meaningful progress is done (with lock).
To do that, pass struct ui_progress to __perf_session__process_event() and set it for the total size of multi-file storage.
--- tools/perf/util/hist.c | 5 ++-- tools/perf/util/hist.h | 3 +- tools/perf/util/session.c | 71 +++++++++++++++++++++++++++++++++++++---------- tools/perf/util/tool.h | 3 ++ 4 files changed, 65 insertions(+), 17 deletions(-)
tools/perf/util/hist.c +3 -2
@@ -1057,12 +1057,13 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)__hists__collapse_resort (hists, root, prog); } -void hists__mt_resort (struct hists *dst,struct hists *src) +void hists__mt_resort (struct hists *dst,struct hists *src, +struct ui_progress *prog) {struct rb_root *root = src->entries_in; sort__need_collapse =1 ; -__hists__collapse_resort (dst, root,NULL ); +__hists__collapse_resort (dst, root, prog); }static int hist_entry__sort (struct hist_entry *a,struct hist_entry *b)
tools/perf/util/hist.h +2 -1
@@ -123,7 +123,8 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,void hist_entry__delete (struct hist_entry *he);void hists__output_resort (struct hists *hists,struct ui_progress *prog); -void hists__mt_resort (struct hists *dst,struct hists *src); +void hists__mt_resort (struct hists *dst,struct hists *src, +struct ui_progress *prog);void hists__collapse_resort (struct hists *hists,struct ui_progress *prog);void hists__decay_entries (struct hists *hists,bool zap_user,bool zap_kernel);
tools/perf/util/session.c +57 -14
193 lines not on this page · raw message
tools/perf/util/tool.h +3 -0
@@ -3,6 +3,7 @@#include <stdbool.h> #include "util/event.h" +#include "ui/progress.h" struct perf_session ;union perf_event ;
@@ -52,6 +53,8 @@ struct perf_tool_mt {struct events_stats stats;struct hists *hists;struct perf_session *session; +struct ui_progress prog; +struct ui_progress *global_prog;int idx;void *priv;
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 36/38 perf report: Add --multi-thread option and config item
In reply to Namhyung KimThe --multi-thread option is to enable parallel processing so user can force serial processing even for indexed data file. It default to false for now but users also can changes this by setting "report.multi_thread" config option in ~/.perfconfig file.
--- tools/perf/Documentation/perf-report.txt | 3 ++ tools/perf/builtin-report.c | 66 +++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 9 deletions(-)
tools/perf/Documentation/perf-report.txt +3 -0
@@ -318,6 +318,9 @@ OPTIONS --header-only:: Show only perf.data header (forces --stdio). +--multi-thread:: + Speed up report by parallelizing sample processing using multi-thread. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-annotate[1]
tools/perf/builtin-report.c +57 -9
118 lines not on this page · raw message
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 37/38 perf session: Handle index files generally
In reply to Namhyung KimThe current code assumes that the number of index item and cpu are matched so it creates that number of threads. But it's not the case of non-system-wide session or data came from different machine.
Just creates threads at most number of online cpus and process data.
--- tools/perf/util/session.c | 76 +++++++++++++++++++++++++++++++++-------------- tools/perf/util/tool.h | 1 - 2 files changed, 54 insertions(+), 23 deletions(-)
tools/perf/util/session.c +54 -22
@@ -1501,26 +1501,51 @@ static struct ui_progress_ops mt_progress__ops = { .update = mt_progress__update, }; +static int perf_session__get_index (struct perf_session *session) +{ +int ret; +static unsigned index =1 ; +static pthread_mutex_t idx_lock =PTHREAD_MUTEX_INITIALIZER ; + +pthread_mutex_lock (&idx_lock); +if (index < session->header.nr_index) + ret = index++; +else + ret =-1 ; +pthread_mutex_unlock (&idx_lock); + +return ret; +} +static void *processing_thread_idx (void *arg) {struct perf_tool_mt *mt_tool = arg;struct perf_session *session = mt_tool->session;int fd =perf_data_file__fd (session->file); -u64 offset = session->header.index[mt_tool->idx].offset; -u64 size = session->header.index[mt_tool->idx].size;u64 file_size =perf_data_file__size (session->file); +int idx; -ui_progress__init (&mt_tool->prog, size,"" ); +while ((idx =perf_session__get_index (session)) >=0 ) { +u64 offset = session->header.index[idx].offset; +u64 size = session->header.index[idx].size; +struct perf_tool_mt *mtt = &mt_tool[idx]; -pr_debug ("processing samples using thread [%d]\n" , mt_tool->idx); -if (__perf_session__process_events (session, &mt_tool->stats, - fd, offset, size, file_size, - &mt_tool->tool, &mt_tool->prog) <0 ) { -pr_err ("processing samples failed (thread [%d])\n" , mt_tool->idx); -return NULL ; +if (size ==0 ) +continue ; + +pr_debug ("processing samples [index %d]\n" , idx); + +ui_progress__init (&mtt->prog, size,"" ); + +if (__perf_session__process_events (session, &mtt->stats, + fd, offset, size, file_size, + &mtt->tool, &mtt->prog) <0 ) { +pr_err ("processing samples failed [index %d]\n" , idx); +return NULL ; + } +pr_debug ("processing samples done [index %d]\n" , idx); } -pr_debug ("processing samples done for thread [%d]\n" , mt_tool->idx);return arg; }
@@ -1538,6 +1563,7 @@ int perf_session__process_events_mt(struct perf_session *session,int err, i, k;int nr_index = session->header.nr_index;u64 size =perf_data_file__size (file); +int nr_thread =sysconf (_SC_NPROCESSORS_ONLN);if (perf_data_file__is_pipe (file) || !session->header.index) {pr_err ("data file doesn't contain the index table\n" );
@@ -1561,15 +1587,11 @@ int perf_session__process_events_mt(struct perf_session *session, ui_progress__ops = &mt_progress__ops; ui_progress__ops->finish = orig_progress__ops->finish; - th_id =calloc (nr_index,sizeof (*th_id)); -if (th_id ==NULL ) - goto out; - mt_tools =calloc (nr_index,sizeof (*mt_tools));if (mt_tools ==NULL ) goto out; -for (i =1 ; i < nr_index; i++) { +for (i =0 ; i < nr_index; i++) { mt = &mt_tools[i];memcpy (&mt->tool, tool,sizeof (*tool));
@@ -1583,20 +1605,30 @@ int perf_session__process_events_mt(struct perf_session *session, mt->session = session; mt->tool.ordered_events = false; - mt->idx = i; mt->priv = arg; mt->global_prog = &prog; - -pthread_create (&th_id[i],NULL , processing_thread_idx, mt); } -for (i =1 ; i < nr_index; i++) { +if (nr_thread > nr_index -1 ) + nr_thread = nr_index -1 ; + + th_id =calloc (nr_thread,sizeof (*th_id)); +if (th_id ==NULL ) + goto out; + +for (i =0 ; i < nr_thread; i++) +pthread_create (&th_id[i],NULL , processing_thread_idx, mt_tools); + +for (i =0 ; i < nr_thread; i++) {pthread_join (th_id[i], (void **)&mt);if (mt ==NULL ) { err = -EINVAL ;continue ; } + } +for (i =0 ; i < nr_index; i++) { + mt = &mt_tools[i];events_stats__add (&evlist->stats, &mt->stats);evlist__for_each (evlist, evsel) {
@@ -1612,7 +1644,7 @@ int perf_session__process_events_mt(struct perf_session *session, ui_progress__ops = orig_progress__ops;ui_progress__init (&prog, nr_entries,"Merging related events..." ); -for (i =1 ; i < nr_index; i++) { +for (i =0 ; i < nr_index; i++) { mt = &mt_tools[i];evlist__for_each (evlist, evsel) {
@@ -1640,7 +1672,7 @@ int perf_session__process_events_mt(struct perf_session *session,perf_tool__warn_about_errors (tool, &evlist->stats);if (mt_tools) { -for (i =1 ; i < nr_index; i++) +for (i =0 ; i < nr_index; i++)free (mt_tools[i].hists);free (mt_tools); }
@@ -1649,7 +1681,7 @@ int perf_session__process_events_mt(struct perf_session *session,return err; err: -while (i-- >1 ) { +while (i-- >0 ) {pthread_cancel (th_id[i]);pthread_join (th_id[i],NULL ); }
tools/perf/util/tool.h +0 -1
@@ -55,7 +55,6 @@ struct perf_tool_mt {struct perf_session *session;struct ui_progress prog;struct ui_progress *global_prog; -int idx;void *priv; };
Signature · 1 line
2.2.2
Namhyung Kim
maintainerPATCH 38/38 perf data: Implement 'index' subcommand
In reply to Namhyung KimThe index command first splits a given data file into intermediate data files and merges them into a final data file with an index table so that it can processed using multi threads. The HEADER_DATA_INDEX feature bit is added to distinguish data file that has an index table.
--- tools/perf/Documentation/perf-data.txt | 25 ++- tools/perf/builtin-data.c | 349 ++++++++++++++++++++++++++++++++- 2 files changed, 370 insertions(+), 4 deletions(-)
tools/perf/Documentation/perf-data.txt +24 -1
@@ -22,6 +22,11 @@ COMMANDS like: perf --debug data-convert data convert ... +index:: + Build an index table for data file so that it can be processed + with multiple threads concurrently. + + OPTIONS for 'convert' --------------------- --to-ctf::
@@ -34,7 +39,25 @@ OPTIONS for 'convert' --verbose:: Be more verbose (show counter open errors, etc). +OPTIONS for 'index' +------------------- +-i:: +--input:: + Specify input perf data file path. + +-o:: +--output:: + Specify output perf data directory path. + +-v:: +--verbose:: + Be more verbose (show counter open errors, etc). + +-f:: +--force:: + Don't complain, do it. + SEE ALSO -------- -linkperf:perf[1] +linkperf:perf[1], linkperf:perf-report[1] [1] Common Trace Format - http://www.efficios.com/ctf
tools/perf/builtin-data.c +346 -3
374 lines not on this page · raw message
Signature · 1 line
2.2.2
Namhyung Kim
maintainercover perf tools: Speed-up perf report by using multi thread (v4)
Hello,
This patchset converts perf report to use multiple threads in order to speed up the processing on large data files. I can see a minimum ~30% of speedup with this change. The code is still experimental and contains many rough edges. But I'd like to share and give some feedbacks.
* changes in v4)
- rename to *_find(new)_by_time()
- also sort struct map by time
- use 'perf_has_index' global variable
- fix an off-by-one bug in index generation
- rebased onto the recent atomic thread refcounting
- remove missing threads tree in favor of thread rwlock
* changes in v3)
- handle header (metadata) same as sample data (at index 0)
- maintain libunwind address space in map_groups instead of thread
- use *_time API only for indexed data file
- resolve callchain with the *_time API
- use dso__data_get/put_fd() to protect access to fd
- synthesize COMM event for command line workload
* changes in v2)
- rework with single indexed data file rather than multiple files in a directory
The perf report processes (sample) events like below:
1. preprocess sample to get matching thread/dso/symbol info
2. insert it to hists rbtree (with callchain tree) based on the info
3. optionally collapse hist entries that match given sort key(s)
4. resort hist entries (by overhead) for output
5. display the hist entries
The stage 1 is a preprocessing and mostly act like a read-only operation in that it doesn't change a machine state during the sample processing. Meta events like fork, comm and mmap can change the machine/thread state but symbols can be loaded during the processing (stage 2).
The stage 2 consumes most of the time especially with callchains and
--children option is enabled. And this work can be easily patitioned
as each sample is independent to others. But the resulting hists must be combined/collapsed to a single global hists before going to further steps.
The stage 3 is optional and only needed by certain sort keys - but with stage 2 paralellized, it needs to be done always.
The stage 4 and 5 works on whole hists so must be done serially.
So my approach is like this:
Partially do stage 1 first - but only for meta events that changes machine state. To do this I add a dummy tracking event to perf record and make it collect such meta events only. They are saved as normal data and processed before sample events at perf report time.
This also requires to handle multiple sample data concurrently and to find a corresponding machine state when processing samples. On a large profiling session, many tasks were created and exited so pid might be recycled (even more than once!). To deal with it, I managed to have thread, map_groups, map and comm in time sorted. The only remaining thing is symbol loading as it's done lazily when sample requires it.
With that being done, the stage 2 can be done by multiple threads. I also save each sample data (per-cpu or per-thread) in separate files during record and then merge them into a single data file with an index table. On perf report time, each region of sample data will be processed by each thread. And symbol loading is protected by a mutex lock.
For DWARF post-unwinding, dso cache data also needs to be protected by a lock and this caused a huge contention. I made it to search the rbtree speculatively first and then, if it didn't find one, search it again under the dso lock. Please take a look at it if it's acceptable.
The patch 1-8 are to support indexing for data file. With --index option, perf record will create a intermediate directory and then save meta events and sample data to separate files. And finally it'll build an index table and concatenate the data files (and also remove the intermediate direcotry).
The patch 9-24 are to manage machine and thread state using timestamp so that it can be searched when processing samples. The patch 25-38 are to implement parallel report. And finally I implemented 'perf data index' command to build an index table for a given data file. The last patch is just to prevent segfault due to a bug in thread refcounting and not inteneded to be merged.
This patchset didn't change perf record to use multi-thread. But I think it can be easily done later if needed.
Note that output has a slight difference to original version when compared using indexed data file. But they're mostly unresolved symbols for callchains.
Here is the result:
This is just elapsed time measured by 'perf stat -r 5'. Note that overall performance in v4 slows down than v3 by ~30% possibly due to atomic thread refcounting and rwlock acquisition.
The data file was recorded during kernel build with fp callchain and size is 2.1GB. The machine has 6 core with hyper-threading enabled and I got a similar result on my laptop too.
perf report --children --no-children + --call-graph none
------------- ------------- -------------------
current 368.290435503 122.786548856 51.269541556 with index 339.618194440 101.383647394 35.880577271 + --multi-thread 219.711277265 70.448753814 29.324704738
This result is with 7.7GB data file using libunwind for callchain.
perf report --children --no-children + --call-graph none
------------- ------------- -------------------
current 213.799598463 155.297229724 6.235268506 with index 216.429553998 156.337434110 4.771545122 + --multi-thread 54.892308166 35.090129366 3.415513290
This result is with same file but using libdw for callchain unwind.
perf report --children --no-children + --call-graph none
------------- ------------- -------------------
current 401.405331379 399.054998021 6.015400857 with index 383.643272700 372.205715392 5.050819167 + --multi-thread 176.419091057 139.776442715 3.356360850
On my archlinux system, callchain unwind using libdw is much slower than libunwind. I'm using elfutils version 0.161. Also I don't know why --children takes less time than --no-children. Anyway we can see the --multi-thread performance is much better for each case.
This patch is based on acme/perf/core - commit 70923bd26c73 ("perf
tools: Make flex/bison calls honour V=1"). I've just found that it's updated to apply atomic refcounting to other structs. But as it requires another round of rebase, fix and testing cycles on my side, I'd like to send out the current version before doing that.
You can get it from 'perf/threaded-v4' branch on my tree at:
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Please take a look and play with it. Any comments are welcome! :)
Thanks,
Namhyung
Namhyung Kim (40):
perf tools: Use a software dummy event to track task/mmap events perf tools: Add rm_rf() utility function
perf tools: Introduce copyfile_offset() function
perf tools: Create separate mmap for dummy tracking event perf tools: Introduce perf_evlist__mmap_track()
perf tools: Add HEADER_DATA_INDEX feature
perf tools: Handle indexed data file properly
perf record: Add --index option for building index table perf report: Skip dummy tracking event
perf tools: Introduce thread__comm(_str)_by_time() helpers perf tools: Add a test case for thread comm handling
perf tools: Use thread__comm_by_time() when adding hist entries perf tools: Convert dead thread list into rbtree
perf tools: Introduce machine__find*_thread_by_time()
perf tools: Add a test case for timed thread handling
perf tools: Reducing arguments of hist_entry_iter__add() perf tools: Maintain map groups list in a leader thread perf tools: Introduce thread__find_addr_location_by_time() and friends perf callchain: Use thread__find_addr_location_by_time() and friends perf tools: Add a test case for timed map groups handling perf tools: Save timestamp of a map creation
perf tools: Introduce map_groups__{insert,find}_by_time() perf tools: Use map_groups__find_addr_by_time()
perf tools: Add testcase for managing maps with time
perf tools: Protect dso symbol loading using a mutex
perf tools: Protect dso cache tree using dso->lock
perf tools: Protect dso cache fd with a mutex
perf callchain: Maintain libunwind's address space in map_groups perf tools: Add dso__data_get/put_fd()
perf session: Pass struct events stats to event processing functions perf hists: Pass hists struct to hist_entry_iter struct perf tools: Move BUILD_ID_SIZE definition to perf.h
perf session: Separate struct machines from session
perf report: Parallelize perf report using multi-thread perf record: Synthesize COMM event for a command line workload perf tools: Fix progress ui to support multi thread
perf report: Add --multi-thread option and config item perf session: Handle index files generally
perf data: Implement 'index' subcommand
perf tools: Disable thread refcount due to bug
tools/perf/Documentation/perf-data.txt | 25 +- tools/perf/Documentation/perf-record.txt | 4 + tools/perf/Documentation/perf-report.txt | 2 + tools/perf/builtin-annotate.c | 7 +- tools/perf/builtin-data.c | 351 +++++++++++++++++- tools/perf/builtin-diff.c | 8 +- tools/perf/builtin-kmem.c | 10 +- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-record.c | 202 ++++++++++- tools/perf/builtin-report.c | 81 ++++- tools/perf/builtin-top.c | 16 +- tools/perf/builtin-trace.c | 2 +- tools/perf/perf.c | 1 + tools/perf/perf.h | 4 + tools/perf/tests/Build | 4 + tools/perf/tests/builtin-test.c | 16 + tools/perf/tests/dwarf-unwind.c | 12 +- tools/perf/tests/hists_common.c | 3 +- tools/perf/tests/hists_cumulate.c | 7 +- tools/perf/tests/hists_filter.c | 5 +- tools/perf/tests/hists_link.c | 6 +- tools/perf/tests/hists_output.c | 7 +- tools/perf/tests/perf-targz-src-pkg | 21 -- tools/perf/tests/tests.h | 4 + tools/perf/tests/thread-comm.c | 47 +++ tools/perf/tests/thread-lookup-time.c | 179 ++++++++++ tools/perf/tests/thread-map-time.c | 90 +++++ tools/perf/tests/thread-mg-share.c | 7 +- tools/perf/tests/thread-mg-time.c | 93 +++++ tools/perf/ui/browsers/hists.c | 30 +- tools/perf/ui/gtk/hists.c | 3 + tools/perf/util/build-id.c | 16 +- tools/perf/util/build-id.h | 2 - tools/perf/util/dso.c | 174 ++++++--- tools/perf/util/dso.h | 11 +- tools/perf/util/event.c | 142 +++++++- tools/perf/util/event.h | 6 +- tools/perf/util/evlist.c | 168 +++++++-- tools/perf/util/evlist.h | 14 +- tools/perf/util/evsel.h | 15 + tools/perf/util/header.c | 64 ++++ tools/perf/util/header.h | 3 + tools/perf/util/hist.c | 123 ++++--- tools/perf/util/hist.h | 8 +- tools/perf/util/machine.c | 291 ++++++++++++--- tools/perf/util/machine.h | 14 +- tools/perf/util/map.c | 72 +++- tools/perf/util/map.h | 37 +- tools/perf/util/probe-event.c | 2 +- .../util/scripting-engines/trace-event-python.c | 4 +- tools/perf/util/session.c | 396 ++++++++++++++++++--- tools/perf/util/session.h | 7 +- tools/perf/util/symbol-elf.c | 2 +- tools/perf/util/symbol.c | 38 +- tools/perf/util/thread.c | 207 ++++++++++- tools/perf/util/thread.h | 28 +- tools/perf/util/tool.h | 14 + tools/perf/util/unwind-libdw.c | 12 +- tools/perf/util/unwind-libunwind.c | 93 ++--- tools/perf/util/unwind.h | 15 +- tools/perf/util/util.c | 81 ++++- tools/perf/util/util.h | 2 + 62 files changed, 2856 insertions(+), 454 deletions(-) delete mode 100755 tools/perf/tests/perf-targz-src-pkg create mode 100644 tools/perf/tests/thread-comm.c create mode 100644 tools/perf/tests/thread-lookup-time.c create mode 100644 tools/perf/tests/thread-map-time.c create mode 100644 tools/perf/tests/thread-mg-time.c
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 1/40 perf tools: Use a software dummy event to track task/mmap events
Add APIs for software dummy event to track task/comm/mmap events separately. The perf record will use them to save such events in a separate mmap buffer to make it easy to index. This is a preparation of multi-thread support which will come later.
--- tools/perf/util/evlist.c | 30 ++++++++++++++++++++++++++++++ tools/perf/util/evlist.h | 1 + tools/perf/util/evsel.h | 15 +++++++++++++++ 3 files changed, 46 insertions(+)
tools/perf/util/evlist.c +30 -0
@@ -194,6 +194,36 @@ int perf_evlist__add_default(struct perf_evlist *evlist)return -ENOMEM ; } +int perf_evlist__add_dummy_tracking (struct perf_evlist *evlist) +{ +struct perf_event_attr attr = { + .type =PERF_TYPE_SOFTWARE , + .config =PERF_COUNT_SW_DUMMY , + .exclude_kernel =1 , + }; +struct perf_evsel *evsel; + +event_attr_init (&attr); + + evsel =perf_evsel__new (&attr); +if (evsel ==NULL ) + goto error; + +/* use strdup() because free(evsel) assumes name is allocated */ + evsel->name =strdup ("dummy" ); +if (!evsel->name) + goto error_free; + +perf_evlist__add (evlist, evsel); +perf_evlist__set_tracking_event (evlist, evsel); + +return 0 ; +error_free: +perf_evsel__delete (evsel); +error: +return -ENOMEM ; +} +static int perf_evlist__add_attrs (struct perf_evlist *evlist,struct perf_event_attr *attrs,size_t nr_attrs) {
tools/perf/util/evlist.h +1 -0
@@ -70,6 +70,7 @@ void perf_evlist__delete(struct perf_evlist *evlist);void perf_evlist__add (struct perf_evlist *evlist,struct perf_evsel *entry);int perf_evlist__add_default (struct perf_evlist *evlist); +int perf_evlist__add_dummy_tracking (struct perf_evlist *evlist);int __perf_evlist__add_default_attrs (struct perf_evlist *evlist,struct perf_event_attr *attrs,size_t nr_attrs);
tools/perf/util/evsel.h +15 -0
@@ -331,6 +331,21 @@ static inline bool perf_evsel__is_function_event(struct perf_evsel *evsel)#undef FUNCTION_EVENT } +/** +* perf_evsel__is_dummy_tracking - Return whether given evsel is a dummy +* event for tracking meta events only +* +* @evsel - evsel selector to be tested +* +* Return %true if event is a dummy tracking event +*/ +static inline bool perf_evsel__is_dummy_tracking (struct perf_evsel *evsel) +{ +return evsel->attr.type ==PERF_TYPE_SOFTWARE && + evsel->attr.config ==PERF_COUNT_SW_DUMMY && + evsel->attr.task ==1 && evsel->attr.mmap ==1 ; +} +struct perf_attr_details {bool freq;bool verbose;
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 2/40 perf tools: Add rm_rf() utility function
In reply to Namhyung KimThe rm_rf() function does same as the shell command 'rm -rf' which removes all directory entries recursively.
--- tools/perf/util/util.c | 43 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/util.h | 1 + 2 files changed, 44 insertions(+)
tools/perf/util/util.c +43 -0
@@ -72,6 +72,49 @@ int mkdir_p(char *path, mode_t mode)return (stat (path, &st) &&mkdir (path, mode)) ?-1 :0 ; } +int rm_rf (char *path) +{ +DIR *dir; +int ret =0 ; +struct dirent *d; +char namebuf[PATH_MAX ]; + + dir =opendir (path); +if (dir ==NULL ) +return 0 ; + +while ((d =readdir (dir)) !=NULL && !ret) { +struct stat statbuf; + +if (!strcmp (d->d_name,"." ) || !strcmp (d->d_name,".." )) +continue ; + +scnprintf (namebuf,sizeof (namebuf),"%s/%s" , + path, d->d_name); + + ret =stat (namebuf, &statbuf); +if (ret <0 ) { +pr_debug ("stat failed: %s\n" , namebuf); +break ; + } + +if (S_ISREG (statbuf.st_mode)) + ret =unlink (namebuf); +else if (S_ISDIR (statbuf.st_mode)) + ret =rm_rf (namebuf); +else { +pr_debug ("unknown file: %s\n" , namebuf); + ret =-1 ; + } + } +closedir (dir); + +if (ret <0 ) +return ret; + +return rmdir (path); +} +static int slow_copyfile (const char *from,const char *to,mode_t mode) {int err =-1 ;
tools/perf/util/util.h +1 -0
@@ -249,6 +249,7 @@ static inline int sane_case(int x, int high) }int mkdir_p (char *path,mode_t mode); +int rm_rf (char *path);int copyfile (const char *from,const char *to);int copyfile_mode (const char *from,const char *to,mode_t mode);
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 3/40 perf tools: Introduce copyfile_offset() function
In reply to Namhyung KimThe copyfile_offset() function is to copy source data from given offset to a destination file with an offset. It'll be used to build an indexed data file.
--- tools/perf/util/util.c | 38 +++++++++++++++++++++++++++++--------- tools/perf/util/util.h | 1 + 2 files changed, 30 insertions(+), 9 deletions(-)
tools/perf/util/util.c +29 -9
@@ -145,11 +145,38 @@ static int slow_copyfile(const char *from, const char *to, mode_t mode)return err; } +int copyfile_offset (int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size) +{ +void *ptr; +loff_t pgoff; + + pgoff = off_in & ~(page_size -1 ); + off_in -= pgoff; + + ptr =mmap (NULL , off_in + size,PROT_READ ,MAP_PRIVATE , ifd, pgoff); +if (ptr ==MAP_FAILED ) +return -1 ; + +while (size) { +ssize_t ret =pwrite (ofd, ptr + off_in, size, off_out); +if (ret <0 && errno ==EINTR ) +continue ; +if (ret <=0 ) +break ; + + size -= ret; + off_in += ret; + off_out -= ret; + } +munmap (ptr, off_in + size); + +return size ?-1 :0 ; +} +int copyfile_mode (const char *from,const char *to,mode_t mode) {int fromfd, tofd;struct stat st; -void *addr;int err =-1 ;if (stat (from, &st))
@@ -166,15 +193,8 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)if (tofd <0 ) goto out_close_from; - addr =mmap (NULL , st.st_size,PROT_READ ,MAP_PRIVATE , fromfd,0 ); -if (addr ==MAP_FAILED ) - goto out_close_to; - -if (write (tofd, addr, st.st_size) == st.st_size) - err =0 ; + err =copyfile_offset (fromfd,0 , tofd,0 , st.st_size); -munmap (addr, st.st_size); -out_close_to:close (tofd);if (err)unlink (to);
tools/perf/util/util.h +1 -0
@@ -252,6 +252,7 @@ int mkdir_p(char *path, mode_t mode);int rm_rf (char *path);int copyfile (const char *from,const char *to);int copyfile_mode (const char *from,const char *to,mode_t mode); +int copyfile_offset (int fromfd,loff_t from_ofs,int tofd,loff_t to_ofs,u64 size);s64 perf_atoll (const char *str);char **argv_split (const char *str,int *argcp);
Signature · 1 line
2.4.0
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 03/40] perf tools: Introduce copyfile_offset() function
Em Mon, May 18, 2015 at 09:30:18AM +0900, Namhyung Kim escreveu:
Quoted 7 lines
The
copyfile_offset()function is to copy source data from given offset to a destination file with an offset. It'll be used to build an indexed data file.Signed-off-by Namhyung Kim <[email protected]>From looking at this patchkit before I recalled that some got Acks, from
Jiri, please try to collect those :-)
Doing it now, applying this patch.
- Arnaldo
Quoted 80 lines
--- tools/perf/util/util.c | 38 +++++++++++++++++++++++++++++--------- tools/perf/util/util.h | 1 + 2 files changed, 30 insertions(+), 9 deletions(-)tools/perf/util/util.c +29
-9@@ -145,11 +145,38 @@ static int slow_copyfile(const char *from, const char *to, mode_t mode)return err; } +int copyfile_offset (int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size) +{ +void *ptr; +loff_t pgoff; + + pgoff = off_in & ~(page_size -1 ); + off_in -= pgoff; + + ptr =mmap (NULL , off_in + size,PROT_READ ,MAP_PRIVATE , ifd, pgoff); +if (ptr ==MAP_FAILED ) +return -1 ; + +while (size) { +ssize_t ret =pwrite (ofd, ptr + off_in, size, off_out); +if (ret <0 && errno ==EINTR ) +continue ; +if (ret <=0 ) +break ; + + size -= ret; + off_in += ret; + off_out -= ret; + } +munmap (ptr, off_in + size); + +return size ?-1 :0 ; +} +int copyfile_mode (const char *from,const char *to,mode_t mode) {int fromfd, tofd;struct stat st; -void *addr;int err =-1 ;if (stat (from, &st))@@ -166,15 +193,8 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)if (tofd <0 ) goto out_close_from; - addr =mmap (NULL , st.st_size,PROT_READ ,MAP_PRIVATE , fromfd,0 ); -if (addr ==MAP_FAILED ) - goto out_close_to; - -if (write (tofd, addr, st.st_size) == st.st_size) - err =0 ; + err =copyfile_offset (fromfd,0 , tofd,0 , st.st_size); -munmap (addr, st.st_size); -out_close_to:close (tofd);if (err)unlink (to);tools/perf/util/util.h +1
-0@@ -252,6 +252,7 @@ int mkdir_p(char *path, mode_t mode);int rm_rf (char *path);int copyfile (const char *from,const char *to);int copyfile_mode (const char *from,const char *to,mode_t mode); +int copyfile_offset (int fromfd,loff_t from_ofs,int tofd,loff_t to_ofs,u64 size);s64 perf_atoll (const char *str);char **argv_split (const char *str,int *argcp);Signature · 1 line
2.4.0
Namhyung Kim
maintainerRe: [PATCH 03/40] perf tools: Introduce copyfile_offset() function
Quoted 9 lines · Arnaldo Carvalho de Melo
Em Mon, May 18, 2015 at 09:30:18AM +0900, Namhyung Kim escreveu:
Quoted 5 lines
The
copyfile_offset()function is to copy source data from given offset to a destination file with an offset. It'll be used to build an indexed data file.Signed-off-by Namhyung Kim <[email protected]>From looking at this patchkit before I recalled that some got Acks, from Jiri, please try to collect those :-)
Ah, right. I completely forgot about it, sorry..
Quoted 2 lines
Doing it now, applying this patch.
Thank you!
Namhyung
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf tools: Introduce copyfile_offset() function
In reply to Namhyung Kimperf tools: Introduce copyfile_offset() function
Commit-ID: 9c9f5a2f1944e8b6bf2b618d04b31e1c1760637e Gitweb: http://git.kernel.org/tip/9c9f5a2f1944e8b6bf2b618d04b31e1c1760637e Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 18 May 2015 09:30:18 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Mon, 18 May 2015 10:17:35 -0300
perf tools: Introduce copyfile_offset() function
The copyfile_offset() function is to copy source data from given offset to a destination file with an offset. It'll be used to build an indexed data file.
--- tools/perf/util/util.c | 38 +++++++++++++++++++++++++++++--------- tools/perf/util/util.h | 1 + 2 files changed, 30 insertions(+), 9 deletions(-)
tools/perf/util/util.c +29 -9
@@ -145,11 +145,38 @@ out:return err; } +int copyfile_offset (int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size) +{ +void *ptr; +loff_t pgoff; + + pgoff = off_in & ~(page_size -1 ); + off_in -= pgoff; + + ptr =mmap (NULL , off_in + size,PROT_READ ,MAP_PRIVATE , ifd, pgoff); +if (ptr ==MAP_FAILED ) +return -1 ; + +while (size) { +ssize_t ret =pwrite (ofd, ptr + off_in, size, off_out); +if (ret <0 && errno ==EINTR ) +continue ; +if (ret <=0 ) +break ; + + size -= ret; + off_in += ret; + off_out -= ret; + } +munmap (ptr, off_in + size); + +return size ?-1 :0 ; +} +int copyfile_mode (const char *from,const char *to,mode_t mode) {int fromfd, tofd;struct stat st; -void *addr;int err =-1 ;if (stat (from, &st))
@@ -166,15 +193,8 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)if (tofd <0 ) goto out_close_from; - addr =mmap (NULL , st.st_size,PROT_READ ,MAP_PRIVATE , fromfd,0 ); -if (addr ==MAP_FAILED ) - goto out_close_to; - -if (write (tofd, addr, st.st_size) == st.st_size) - err =0 ; + err =copyfile_offset (fromfd,0 , tofd,0 , st.st_size); -munmap (addr, st.st_size); -out_close_to:close (tofd);if (err)unlink (to);
tools/perf/util/util.h +1 -0
@@ -252,6 +252,7 @@ int mkdir_p(char *path, mode_t mode);int rm_rf (char *path);int copyfile (const char *from,const char *to);int copyfile_mode (const char *from,const char *to,mode_t mode); +int copyfile_offset (int fromfd,loff_t from_ofs,int tofd,loff_t to_ofs,u64 size);s64 perf_atoll (const char *str);char **argv_split (const char *str,int *argcp);
Namhyung Kim
maintainerPATCH 4/40 perf tools: Create separate mmap for dummy tracking event
In reply to Namhyung KimWhen indexed data file support is enabled, a dummy tracking event will be used to track metadata (like task, comm and mmap events) for a session and actual samples will be recorded in separate (intermediate) files and then merged (with index table).
Provide separate mmap to the dummy tracking event. The size is fixed to 128KiB (+ 1 page) as the event rate will be lower than samples. I originally wanted to use a single mmap for this but cross-cpu sharing is prohibited so it's per-cpu (or per-task) like normal mmaps.
--- tools/perf/builtin-record.c | 10 +++- tools/perf/util/evlist.c | 131 +++++++++++++++++++++++++++++++++++--------- tools/perf/util/evlist.h | 11 +++- 3 files changed, 123 insertions(+), 29 deletions(-)
tools/perf/builtin-record.c +9 -1
@@ -71,7 +71,7 @@ static int process_synthesized_event(struct perf_tool *tool,static int record__mmap_read (struct record *rec,int idx) { -struct perf_mmap *md = &rec->evlist->mmap[idx]; +struct perf_mmap *md =perf_evlist__mmap_desc (rec->evlist, idx);u64 head =perf_mmap__read_head (md);u64 old = md->prev;unsigned char *data = md->base + page_size;
@@ -107,6 +107,7 @@ static int record__mmap_read(struct record *rec, int idx) } md->prev = old; +perf_evlist__mmap_consume (rec->evlist, idx); out:return rc;
@@ -414,6 +415,13 @@ static int record__mmap_read_all(struct record *rec) } } +if (rec->evlist->track_mmap[i].base) { +if (record__mmap_read (rec,track_mmap_idx (i)) !=0 ) { + rc =-1 ; + goto out; + } + } +if (mm->base && !rec->opts.auxtrace_snapshot_mode &&record__auxtrace_mmap_read (rec, mm) !=0 ) { rc =-1 ;
tools/perf/util/evlist.c +104 -27
244 lines not on this page · raw message
tools/perf/util/evlist.h +10 -1
@@ -50,12 +50,15 @@ struct perf_evlist {bool overwrite;struct fdarray pollfd;struct perf_mmap *mmap; +struct perf_mmap *track_mmap;struct thread_map *threads;struct cpu_map *cpus;struct perf_evsel *selected;struct events_stats stats; }; +#define TRACK_MMAP_SIZE (((128 * 1024 / page_size) + 1) * page_size) +struct perf_evsel_str_handler {const char *name;void *handler;
@@ -105,8 +108,8 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);struct perf_sample_id *perf_evlist__id2sid (struct perf_evlist *evlist,u64 id);union perf_event *perf_evlist__mmap_read (struct perf_evlist *evlist,int idx); -void perf_evlist__mmap_consume (struct perf_evlist *evlist,int idx); +struct perf_mmap *perf_evlist__mmap_desc (struct perf_evlist *evlist,int idx);int perf_evlist__open (struct perf_evlist *evlist);void perf_evlist__close (struct perf_evlist *evlist);
@@ -219,6 +222,12 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str);void perf_evlist__to_front (struct perf_evlist *evlist,struct perf_evsel *move_evsel); +/* convert from/to negative idx for track mmaps */ +static inline int track_mmap_idx (int idx) +{ +return -idx -1 ; +} +/** * __evlist__for_each - iterate thru all the evsels * @list: list_head instance to iterate
Signature · 1 line
2.4.0
Re: [PATCH 04/40] perf tools: Create separate mmap for dummy tracking event
On Mon, May 18, 2015 at 09:30:19AM +0900, Namhyung Kim wrote:
SNIP
Quoted 24 lines
tools/perf/util/evlist.c +13
-3-static int perf_evlist__alloc_mmap (struct perf_evlist *evlist) +static int perf_evlist__alloc_mmap (struct perf_evlist *evlist,bool track_mmap) { evlist->nr_mmaps =cpu_map__nr (evlist->cpus);if (cpu_map__empty (evlist->cpus)) evlist->nr_mmaps =thread_map__nr (evlist->threads); evlist->mmap =zalloc (evlist->nr_mmaps *sizeof (struct perf_mmap )); -return evlist->mmap !=NULL ?0 : -ENOMEM ; +if (evlist->mmap ==NULL ) +return -ENOMEM ; + +if (track_mmap) { + evlist->track_mmap =calloc (evlist->nr_mmaps, +sizeof (struct perf_mmap )); +if (evlist->track_mmap ==NULL ) { +zfree (&evlist->mmap); +return -ENOMEM ; + } + } +return 0 ; }struct mmap_params { -int prot;
tools/perf/util/evlist.c void perf_evlist__munmap(struct perf_evlist *evlist) · line 872
Quoted 10 lines
tools/perf/util/evlist.c +4
-2-int mask; +int prot; +size_t len;struct auxtrace_mmap_params auxtrace_mp; }; -static int __perf_evlist__mmap (struct perf_evlist *evlist,int idx, +static int __perf_evlist__mmap (struct perf_evlist *evlist__maybe_unused , +struct perf_mmap *pmmap,struct mmap_params *mp,int fd)
hum, looks like this patch should be separated to:
- one that makes __perf_evlist__mmap use perf_mmap directly and moves auxtrace_mmap__mmap..
also __perf_evlist__mmap should be renamed (perf_mmap__mmap? ;-) ) and evlist arg removed, because it's not used..
- one that adds dummy tracing events mmapping
maybe also separate the code that alloc and free evlist->track_mmap with perf_evlist__mmap_desc stuff, might help the readability..
jirka
Namhyung Kim
maintainerRe: [PATCH 04/40] perf tools: Create separate mmap for dummy tracking event
Hi Jiri,
Quoted 46 lines · Jiri Olsa
On Mon, May 18, 2015 at 09:30:19AM +0900, Namhyung Kim wrote:
SNIP
Quoted 24 lines
tools/perf/util/evlist.c +13
-3-static int perf_evlist__alloc_mmap (struct perf_evlist *evlist) +static int perf_evlist__alloc_mmap (struct perf_evlist *evlist,bool track_mmap) { evlist->nr_mmaps =cpu_map__nr (evlist->cpus);if (cpu_map__empty (evlist->cpus)) evlist->nr_mmaps =thread_map__nr (evlist->threads); evlist->mmap =zalloc (evlist->nr_mmaps *sizeof (struct perf_mmap )); -return evlist->mmap !=NULL ?0 : -ENOMEM ; +if (evlist->mmap ==NULL ) +return -ENOMEM ; + +if (track_mmap) { + evlist->track_mmap =calloc (evlist->nr_mmaps, +sizeof (struct perf_mmap )); +if (evlist->track_mmap ==NULL ) { +zfree (&evlist->mmap); +return -ENOMEM ; + } + } +return 0 ; }struct mmap_params { -int prot;tools/perf/util/evlist.c void perf_evlist__munmap(struct perf_evlist *evlist) · line 872
Quoted 10 lines
tools/perf/util/evlist.c +4
-2-int mask; +int prot; +size_t len;struct auxtrace_mmap_params auxtrace_mp; }; -static int __perf_evlist__mmap (struct perf_evlist *evlist,int idx, +static int __perf_evlist__mmap (struct perf_evlist *evlist__maybe_unused , +struct perf_mmap *pmmap,struct mmap_params *mp,int fd)hum, looks like this patch should be separated to:
- one that makes
__perf_evlist__mmapuseperf_mmapdirectly and movesauxtrace_mmap__mmap..
also__perf_evlist__mmapshould be renamed (perf_mmap__mmap? ;-) ) and evlist arg removed, because it's not used..
Quoted 4 lines
- one that adds dummy tracing events mmapping
maybe also separate the code that alloc and free
evlist->track_mmapwithperf_evlist__mmap_descstuff, might help the readability..
OK, will do.
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 5/40 perf tools: Introduce perf_evlist__mmap_track()
In reply to Namhyung KimThe perf_evlist__mmap_track function creates data mmaps and optionally tracking mmaps for events. It'll be used for perf record to save events in a separate files and build an index table. Checking dummy tracking event in perf_evlist__mmap() alone is not enough as users can specify a dummy event (like in keep tracking testcase) without the index option.
--- tools/perf/builtin-record.c | 2 +- tools/perf/util/evlist.c | 13 ++++++++----- tools/perf/util/evlist.h | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-)
tools/perf/builtin-record.c +1 -1
@@ -304,7 +304,7 @@ static int record__open(struct record *rec)if (perf_evlist__mmap_ex (evlist, opts->mmap_pages, false, opts->auxtrace_mmap_pages, - opts->auxtrace_snapshot_mode) <0 ) { + opts->auxtrace_snapshot_mode, false) <0 ) {if (errno ==EPERM ) {pr_err ("Permission error mapping pages.\n" "Consider increasing "
tools/perf/util/evlist.c +8 -5
@@ -863,6 +863,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist, bool track_mmap)struct mmap_params {int prot; +bool track;size_t len;struct auxtrace_mmap_params auxtrace_mp; };
@@ -913,7 +914,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, fd =FD (evsel, cpu, thread); -if (perf_evsel__is_dummy_tracking (evsel)) { +if (mp->track &&perf_evsel__is_dummy_tracking (evsel)) {struct mmap_params track_mp = { .prot = mp->prot, .len =TRACK_MMAP_SIZE ,
@@ -971,7 +972,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, thread); } -if (perf_evsel__is_dummy_tracking (evsel)) { +if (mp->track &&perf_evsel__is_dummy_tracking (evsel)) {/* restore idx as normal idx (positive) */ idx =track_mmap_idx (idx); }
@@ -1138,6 +1139,7 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,* @overwrite: overwrite older events? * @auxtrace_pages - auxtrace map length in pages * @auxtrace_overwrite - overwrite older auxtrace data? +* @use_track_mmap: use another mmaps to track meta events * * If @overwrite is %false the user needs to signal event consumption using * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this
@@ -1150,16 +1152,17 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,*/ int perf_evlist__mmap_ex (struct perf_evlist *evlist,unsigned int pages,bool overwrite,unsigned int auxtrace_pages, -bool auxtrace_overwrite) +bool auxtrace_overwrite,bool use_track_mmap) {struct perf_evsel *evsel;const struct cpu_map *cpus = evlist->cpus;const struct thread_map *threads = evlist->threads;struct mmap_params mp = { .prot =PROT_READ | (overwrite ?0 :PROT_WRITE ), + .track = use_track_mmap, }; -if (evlist->mmap ==NULL &&perf_evlist__alloc_mmap (evlist, true) <0 ) +if (evlist->mmap ==NULL &&perf_evlist__alloc_mmap (evlist, mp.track) <0 )return -ENOMEM ;if (evlist->pollfd.entries ==NULL &&perf_evlist__alloc_pollfd (evlist) <0 )
@@ -1189,7 +1192,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,int perf_evlist__mmap (struct perf_evlist *evlist,unsigned int pages,bool overwrite) { -return perf_evlist__mmap_ex (evlist, pages, overwrite,0 , false); +return perf_evlist__mmap_ex (evlist, pages, overwrite,0 , false, false); }int perf_evlist__create_maps (struct perf_evlist *evlist,struct target *target)
tools/perf/util/evlist.h +1 -1
@@ -135,7 +135,7 @@ int perf_evlist__parse_mmap_pages(const struct option *opt,int perf_evlist__mmap_ex (struct perf_evlist *evlist,unsigned int pages,bool overwrite,unsigned int auxtrace_pages, -bool auxtrace_overwrite); +bool auxtrace_overwrite,bool use_track_mmap);int perf_evlist__mmap (struct perf_evlist *evlist,unsigned int pages,bool overwrite);void perf_evlist__munmap (struct perf_evlist *evlist);
Signature · 1 line
2.4.0
Re: [PATCH 05/40] perf tools: Introduce perf_evlist__mmap_track()
Quoted 1 line · Namhyung Kim
The
perf_evlist__mmap_trackfunction creates data mmaps and optionally
hum, there's no perf_evlist__mmap_track in the patch ;-)
you're now using perf_evlist__mmap_ex that was introduced by Adrian IIRC
jirka
Namhyung Kim
maintainerRe: [PATCH 05/40] perf tools: Introduce perf_evlist__mmap_track()
Quoted 4 lines · Jiri Olsa
Quoted 1 line · Namhyung Kim
The
perf_evlist__mmap_trackfunction creates data mmaps and optionallyhum, there's no
perf_evlist__mmap_trackin the patch ;-)
Oops, I missed to update the changelog ;-P
Quoted 2 lines
you're now using
perf_evlist__mmap_exthat was introduced by Adrian IIRC
Right, thanks for letting me know. Will update.
Thanks
Namhyung
Namhyung Kim
maintainerPATCH 6/40 perf tools: Add HEADER_DATA_INDEX feature
In reply to Namhyung KimThe HEADER_DATA_INDEX feature is to record index table for sample data so that they can be processed by multiple thread concurrently. Each item is a struct perf_file_section which consists of an offset and size.
--- tools/perf/builtin-record.c | 2 ++ tools/perf/util/header.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 3 files changed, 67 insertions(+)
tools/perf/builtin-record.c +2 -0
@@ -459,6 +459,8 @@ static void record__init_features(struct record *rec)if (!rec->opts.full_auxtrace)perf_header__clear_feat (&session->header,HEADER_AUXTRACE ); + +perf_header__clear_feat (&session->header,HEADER_DATA_INDEX ); }static volatile int workload_exec_errno;
tools/perf/util/header.c +62 -0
92 lines not on this page · raw message
tools/perf/util/header.h +3 -0
@@ -31,6 +31,7 @@ enum {HEADER_PMU_MAPPINGS ,HEADER_GROUP_DESC ,HEADER_AUXTRACE , +HEADER_DATA_INDEX ,HEADER_LAST_FEATURE , HEADER_FEAT_BITS = 256, };
@@ -95,6 +96,8 @@ struct perf_header {bool needs_swap;u64 data_offset;u64 data_size; +struct perf_file_section *index; +u64 nr_index;u64 feat_offset;DECLARE_BITMAP (adds_features,HEADER_FEAT_BITS );struct perf_session_env env;
Signature · 1 line
2.4.0
Re: [PATCH 06/40] perf tools: Add HEADER_DATA_INDEX feature
On Mon, May 18, 2015 at 09:30:21AM +0900, Namhyung Kim wrote:
SNIP
Quoted 14 lines
tools/perf/util/header.c +13
-0+static int process_data_index (struct perf_file_section *section__maybe_unused , +struct perf_header *ph,int fd, +void *data__maybe_unused ) +{ +ssize_t ret; +u64 nr_idx; +unsigned i; +struct perf_file_section *idx; + + ret =readn (fd, &nr_idx,sizeof (nr_idx)); +if (ret !=sizeof (nr_idx)) +return -1 ; +
tools/perf/util/header.c static int process_auxtrace(struct perf_file_section *section, · line 1907
Quoted 10 lines
tools/perf/util/header.c +10
-0+if (ph->needs_swap) + nr_idx =bswap_64 (nr_idx); + + idx =calloc (nr_idx,sizeof (*idx)); +if (idx ==NULL ) +return -1 ; + +for (i =0 ; i < nr_idx; i++) { + ret =readn (fd, &idx[i],sizeof (*idx)); +if (ret !=sizeof (*idx))
missing free(idx);
jirka
Quoted 11 lines
tools/perf/util/header.c +11
-0+return ret; + +if (ph->needs_swap) { + idx[i].offset =bswap_64 (idx[i].offset); + idx[i].size =bswap_64 (idx[i].size); + } + } + + ph->index = idx; + ph->nr_index = nr_idx; +return 0 ;
SNIP
Namhyung Kim
maintainerRe: [PATCH 06/40] perf tools: Add HEADER_DATA_INDEX feature
Quoted 22 lines · Jiri Olsa
On Mon, May 18, 2015 at 09:30:21AM +0900, Namhyung Kim wrote:
SNIP
Quoted 18 lines
tools/perf/util/header.c +17
-0+static int process_data_index (struct perf_file_section *section__maybe_unused , +struct perf_header *ph,int fd, +void *data__maybe_unused ) +{ +ssize_t ret; +u64 nr_idx; +unsigned i; +struct perf_file_section *idx; + + ret =readn (fd, &nr_idx,sizeof (nr_idx)); +if (ret !=sizeof (nr_idx)) +return -1 ; + +if (ph->needs_swap) + nr_idx =bswap_64 (nr_idx); + + idx =calloc (nr_idx,sizeof (*idx));
Quoted 8 lines
tools/perf/util/header.c static int process_auxtrace(struct perf_file_section *section, · line 1907
Quoted 6 lines
tools/perf/util/header.c +6
-0+if (idx ==NULL ) +return -1 ; + +for (i =0 ; i < nr_idx; i++) { + ret =readn (fd, &idx[i],sizeof (*idx)); +if (ret !=sizeof (*idx))missing free(idx);
You're right. Also it needs to return negative value for error, will update.
Thanks,
Namhyung
Quoted 20 lines
tools/perf/util/header.c static int process_auxtrace(struct perf_file_section *section, · line 1918
Quoted 12 lines
tools/perf/util/header.c +11
-0+return ret; + +if (ph->needs_swap) { + idx[i].offset =bswap_64 (idx[i].offset); + idx[i].size =bswap_64 (idx[i].size); + } + } + + ph->index = idx; + ph->nr_index = nr_idx; +return 0 ;SNIP
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Namhyung Kim
maintainerPATCH 7/40 perf tools: Handle indexed data file properly
In reply to Namhyung KimWhen perf detects data file has index table, process header part first and then rest data files in a row. Note that the indexed sample data is recorded for each cpu/thread separately, it's already ordered with respect to themselves so no need to use the ordered event queue interface.
--- tools/perf/perf.c | 1 + tools/perf/perf.h | 2 ++ tools/perf/util/session.c | 55 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 49 insertions(+), 9 deletions(-)
tools/perf/perf.c +1 -0
@@ -27,6 +27,7 @@ const char perf_more_info_string[] =int use_browser =-1 ;static int use_pager =-1 ;const char *input_name; +bool perf_has_index;struct cmd_struct {const char *cmd;
tools/perf/perf.h +2 -0
@@ -39,6 +39,8 @@ void pthread__unblock_sigwinch(void);#include "util/target.h" +extern bool perf_has_index; +struct record_opts {struct target target;bool group;
tools/perf/util/session.c +46 -9
81 lines not on this page · raw message
Signature · 1 line
2.4.0
Re: [PATCH 07/40] perf tools: Handle indexed data file properly
Quoted 18 lines · Namhyung Kim
When perf detects data file has index table, process header part first and then rest data files in a row. Note that the indexed sample data is recorded for each cpu/thread separately, it's already ordered with respect to themselves so no need to use the ordered event queue interface.
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/perf.c | 1 + tools/perf/perf.h | 2 ++ tools/perf/util/session.c | 55 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 49 insertions(+), 9 deletions(-)tools/perf/perf.c +0
-0
tools/perf/perf.c const char perf_more_info_string[] = · line 30
Quoted 4 lines
tools/perf/perf.c +1
-0@@ -27,6 +27,7 @@ const char perf_more_info_string[] =int use_browser =-1 ;static int use_pager =-1 ;const char *input_name; +bool perf_has_index;
I probably missed some discussion here, but why is this not perf_session function? I saw some non session related functions further in the patchset using this.. is that the reason?
it seems fairly perf_session specific.. might be worth to keep it there? just asking ;-)
jirka
Namhyung Kim
maintainerRe: [PATCH 07/40] perf tools: Handle indexed data file properly
Quoted 28 lines · Jiri Olsa
Quoted 18 lines · Namhyung Kim
When perf detects data file has index table, process header part first and then rest data files in a row. Note that the indexed sample data is recorded for each cpu/thread separately, it's already ordered with respect to themselves so no need to use the ordered event queue interface.
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/perf.c | 1 + tools/perf/perf.h | 2 ++ tools/perf/util/session.c | 55 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 49 insertions(+), 9 deletions(-)tools/perf/perf.c +0
-0tools/perf/perf.c const char perf_more_info_string[] = · line 30
Quoted 4 lines
tools/perf/perf.c +1
-0@@ -27,6 +27,7 @@ const char perf_more_info_string[] =int use_browser =-1 ;static int use_pager =-1 ;const char *input_name; +bool perf_has_index;I probably missed some discussion here, but why is this not
perf_sessionfunction? I saw some non session related functions further in the patchset using this.. is that the reason?
Quoted 2 lines
it seems fairly
perf_sessionspecific.. might be worth to keep it there? just asking ;-)
I agree. But it requires to pass a pointer to session wherever processing samples. Arnaldo said that he wanted to remove accesses to a session in general. So I added the global variable.
https://lkml.org/lkml/2015/3/3/353
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 8/40 perf record: Add --index option for building index table
In reply to Namhyung KimThe new --index option will create indexed data file which can be processed by multiple threads parallelly. It saves meta event and sample data in separate files and merges them with an index table.
If there's an index table in the data file, the HEADER_DATA_INDEX feature bit is set and session->header.index[0] will point to the meta event area, and rest are sample data. It'd look like below:
+---------------------+
| file header |
|---------------------|
| |
| meta events[0] <-+--+
| | |
|---------------------| |
| | |
| sample data[1] <-+--+
| | |
|---------------------| |
| | |
| sample data[2] <-|--+
| | |
|---------------------| |
| ... | ...
|---------------------| |
| feature data | |
| (contains index) -+--+
+---------------------+--- tools/perf/Documentation/perf-record.txt | 4 + tools/perf/builtin-record.c | 172 ++++++++++++++++++++++++++++--- tools/perf/perf.h | 1 + tools/perf/util/header.c | 2 + tools/perf/util/session.c | 1 + 5 files changed, 166 insertions(+), 14 deletions(-)
tools/perf/Documentation/perf-record.txt +4 -0
@@ -267,6 +267,10 @@ AUX area tracing event. Optionally the number of bytes to capture per snapshot can be specified. In Snapshot Mode, trace data is captured only when signal SIGUSR2 is received. +--index:: +Build an index table for sample data. This will speed up perf report by +parallel processing. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-list[1]
tools/perf/builtin-record.c +158 -14
282 lines not on this page · raw message
tools/perf/perf.h +1 -0
@@ -58,6 +58,7 @@ struct record_opts {bool running_time;bool full_auxtrace;bool auxtrace_snapshot_mode; +bool index;unsigned int freq;unsigned int mmap_pages;unsigned int auxtrace_mmap_pages;
tools/perf/util/header.c +2 -0
@@ -2652,6 +2652,8 @@ int perf_session__read_header(struct perf_session *session) session->tevent.pevent)) goto out_delete_evlist; + perf_has_index =perf_header__has_feat (&session->header,HEADER_DATA_INDEX ); +return 0 ; out_errno:return -errno;
tools/perf/util/session.c +1 -0
@@ -195,6 +195,7 @@ void perf_session__delete(struct perf_session *session)machines__exit (&session->machines);if (session->file)perf_data_file__close (session->file); +free (session->header.index);free (session); }
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 9/40 perf report: Skip dummy tracking event
In reply to Namhyung KimThe dummy tracking event is only for tracking task/comom/mmap events and has no sample data for itself. So no need to report, just skip it.
--- tools/perf/builtin-report.c | 3 +++ tools/perf/ui/browsers/hists.c | 30 ++++++++++++++++++++++++------ tools/perf/ui/gtk/hists.c | 3 +++ 3 files changed, 30 insertions(+), 6 deletions(-)
tools/perf/builtin-report.c +3 -0
@@ -323,6 +323,9 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,struct hists *hists =evsel__hists (pos);const char *evname =perf_evsel__name (pos); +if (perf_evsel__is_dummy_tracking (pos)) +continue ; +if (symbol_conf.event_group && !perf_evsel__is_group_leader (pos))continue ;
tools/perf/ui/browsers/hists.c +24 -6
@@ -2128,14 +2128,17 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,return key; } -static bool filter_group_entries (struct ui_browser *browser__maybe_unused , -void *entry) +static bool filter_entries (struct ui_browser *browser__maybe_unused , +void *entry) { struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);if (symbol_conf.event_group && !perf_evsel__is_group_leader (evsel))return true; +if (perf_evsel__is_dummy_tracking (evsel)) +return true; +return false; }
@@ -2152,7 +2155,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, .refresh = ui_browser__list_head_refresh, .seek = ui_browser__list_head_seek, .write = perf_evsel_menu__write, - .filter = filter_group_entries, + .filter = filter_entries, .nr_entries = nr_entries, .priv = evlist, },
@@ -2179,21 +2182,22 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, struct perf_session_env *env) {int nr_entries = evlist->nr_entries; +struct perf_evsel *first =perf_evlist__first (evlist); +struct perf_evsel *pos; single_entry:if (nr_entries ==1 ) { -struct perf_evsel *first =perf_evlist__first (evlist); -return perf_evsel__hists_browse (first, nr_entries, help, false, hbt, min_pcnt, env); }if (symbol_conf.event_group) { -struct perf_evsel *pos; nr_entries =0 ;evlist__for_each (evlist, pos) { +if (perf_evsel__is_dummy_tracking (pos)) +continue ;if (perf_evsel__is_group_leader (pos)) nr_entries++; }
@@ -2202,6 +2206,20 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, goto single_entry; } +evlist__for_each (evlist, pos) { +if (perf_evsel__is_dummy_tracking (pos)) + nr_entries--; + } + +if (nr_entries ==1 ) { +evlist__for_each (evlist, pos) { +if (!perf_evsel__is_dummy_tracking (pos)) { + first = pos; + goto single_entry; + } + } + } +return __perf_evlist__tui_browse_hists (evlist, nr_entries, help, hbt, min_pcnt, env); }
tools/perf/ui/gtk/hists.c +3 -0
@@ -317,6 +317,9 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,char buf[512 ];size_t size =sizeof (buf); +if (perf_evsel__is_dummy_tracking (pos)) +continue ; +if (symbol_conf.event_group) {if (!perf_evsel__is_group_leader (pos))continue ;
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 10/40 perf tools: Introduce thread__comm(_str)_by_time() helpers
In reply to Namhyung KimWhen data file indexing is enabled, it processes all task, comm and mmap events first and then goes to the sample events. So all it sees is the last comm of a thread although it has information at the time of sample.
Sort thread's comm by time so that it can find appropriate comm at the sample time. The thread__comm_by_time() will mostly work even if PERF_SAMPLE_TIME bit is off since in that case, sample->time will be -1 so it'll take the last comm anyway.
--- tools/perf/util/thread.c | 33 ++++++++++++++++++++++++++++++++- tools/perf/util/thread.h | 2 ++ 2 files changed, 34 insertions(+), 1 deletion(-)
tools/perf/util/thread.c +32 -1
@@ -123,6 +123,21 @@ struct comm *thread__exec_comm(const struct thread *thread)return last; } +struct comm *thread__comm_by_time (const struct thread *thread,u64 timestamp) +{ +struct comm *comm; + +list_for_each_entry (comm, &thread->comm_list, list) { +if (timestamp >= comm->start) +return comm; + } + +if (list_empty (&thread->comm_list)) +return NULL ; + + return list_last_entry(&thread->comm_list, struct comm, list); +} +int __thread__set_comm (struct thread *thread,const char *str,u64 timestamp,bool exec) {
@@ -138,7 +153,13 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, new =comm__new (str, timestamp, exec);if (!new)return -ENOMEM ; -list_add (&new->list, &thread->comm_list); + +/* sort by time */ +list_for_each_entry (curr, &thread->comm_list, list) { +if (timestamp >= curr->start) +break ; + } +list_add_tail (&new->list, &curr->list);if (exec)unwind__flush_access (thread);
@@ -159,6 +180,16 @@ const char *thread__comm_str(const struct thread *thread)return comm__str (comm); } +const char *thread__comm_str_by_time (const struct thread *thread,u64 timestamp) +{ +const struct comm *comm =thread__comm_by_time (thread, timestamp); + +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) {
tools/perf/util/thread.h +2 -0
@@ -68,7 +68,9 @@ static inline int thread__set_comm(struct thread *thread, const char *comm,int thread__comm_len (struct thread *thread);struct comm *thread__comm (const struct thread *thread);struct comm *thread__exec_comm (const struct thread *thread); +struct comm *thread__comm_by_time (const struct thread *thread,u64 timestamp);const char *thread__comm_str (const struct thread *thread); +const char *thread__comm_str_by_time (const struct thread *thread,u64 timestamp);void thread__insert_map (struct thread *thread,struct map *map);int thread__fork (struct thread *thread,struct thread *parent,u64 timestamp);size_t thread__fprintf (struct thread *thread,FILE *fp);
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 11/40 perf tools: Add a test case for thread comm handling
In reply to Namhyung KimThe new test case checks various thread comm handling like overridding and time sorting.
--- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 ++++ tools/perf/tests/perf-targz-src-pkg | 21 ----------------- tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-comm.c | 47 +++++++++++++++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 21 deletions(-) delete mode 100755 tools/perf/tests/perf-targz-src-pkg create mode 100644 tools/perf/tests/thread-comm.c
tools/perf/tests/Build +1 -0
@@ -24,6 +24,7 @@ perf-y += bp_signal_overflow.o perf-y += task-exit.o perf-y += sw-clock.o perf-y += mmap-thread-lookup.o +perf-y += thread-comm.o perf-y += thread-mg-share.o perf-y += switch-tracking.o perf-y += keep-tracking.o
tools/perf/tests/builtin-test.c +4 -0
@@ -171,6 +171,10 @@ static struct test { .func = test__kmod_path__parse, }, { + .desc = "Test thread comm handling", + .func = test__thread_comm, + }, + { .func = NULL, }, };
tools/perf/tests/perf-targz-src-pkg deleted +0 -21
@@ -1,21 +0,0 @@ -#!/bin/sh -# Test one of the main kernel Makefile targets to generate a perf sources tarball -# suitable for build outside the full kernel sources. -# -# This is to test that the tools/perf/MANIFEST file lists all the files needed to -# be in such tarball, which sometimes gets broken when we move files around, -# like when we made some files that were in tools/perf/ available to other tools/ -# codebases by moving it to tools/include/, etc. - -PERF=$1 -cd ${PERF}/../.. -make perf-targz-src-pkg > /dev/null -TARBALL=$(ls -rt perf-*.tar.gz) -TMP_DEST=$(mktemp -d) -tar xf ${TARBALL} -C $TMP_DEST -rm -f ${TARBALL} -cd - > /dev/null -make -C $TMP_DEST/perf*/tools/perf > /dev/null 2>&1 -RC=$? -rm -rf ${TMP_DEST} -exit $RC
tools/perf/tests/tests.h +1 -0
@@ -61,6 +61,7 @@ int test__switch_tracking(void);int test__fdarray__filter (void );int test__fdarray__add (void );int test__kmod_path__parse (void ); +int test__thread_comm (void );#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)#ifdef HAVE_DWARF_UNWIND_SUPPORT
tools/perf/tests/thread-comm.c new file +47 -0
47 lines not on this page · raw message
Signature · 1 line
2.4.0
Re: [PATCH 11/40] perf tools: Add a test case for thread comm handling
On Mon, May 18, 2015 at 09:30:26AM +0900, Namhyung Kim wrote:
SNIP
tools/perf/tests/perf-targz-src-pkg
Quoted 20 lines
tools/perf/tests/builtin-test.c +4
-0@@ -171,6 +171,10 @@ static struct test { .func = test__kmod_path__parse, }, { + .desc = "Test thread comm handling", + .func = test__thread_comm, + }, + { .func = NULL, }, };tools/perf/tests/perf-targz-src-pkg deleted +0
-0
you dont like this one? ;-)
jirka
Quoted 22 lines
tools/perf/tests/perf-targz-src-pkg deleted +0
-21@@ -1,21 +0,0 @@ -#!/bin/sh -# Test one of the main kernel Makefile targets to generate a perf sources tarball -# suitable for build outside the full kernel sources. -# -# This is to test that the tools/perf/MANIFEST file lists all the files needed to -# be in such tarball, which sometimes gets broken when we move files around, -# like when we made some files that were in tools/perf/ available to other tools/ -# codebases by moving it to tools/include/, etc. - -PERF=$1 -cd ${PERF}/../.. -make perf-targz-src-pkg > /dev/null -TARBALL=$(ls -rt perf-*.tar.gz) -TMP_DEST=$(mktemp -d) -tar xf ${TARBALL} -C $TMP_DEST -rm -f ${TARBALL} -cd - > /dev/null -make -C $TMP_DEST/perf*/tools/perf > /dev/null 2>&1 -RC=$? -rm -rf ${TMP_DEST} -exit $RC
SNIP
Namhyung Kim
maintainerRe: [PATCH 11/40] perf tools: Add a test case for thread comm handling
Quoted 18 lines · Jiri Olsa
On Mon, May 18, 2015 at 09:30:26AM +0900, Namhyung Kim wrote:
SNIP
Quoted 14 lines
tools/perf/tests/builtin-test.c +4
-0@@ -171,6 +171,10 @@ static struct test { .func = test__kmod_path__parse, }, { + .desc = "Test thread comm handling", + .func = test__thread_comm, + }, + { .func = NULL, },
Quoted 8 lines
tools/perf/tests/perf-targz-src-pkg
Quoted 6 lines
tools/perf/tests/builtin-test.c +0
-0};tools/perf/tests/perf-targz-src-pkg deleted +0
-0you dont like this one? ;-)
Argh, I don't know how it's included in this patch. Will remove :-/
Thanks,
Namhyung
Quoted 32 lines
jirka
Quoted 12 lines
tools/perf/tests/perf-targz-src-pkg deleted +0
-11@@ -1,21 +0,0 @@ -#!/bin/sh -# Test one of the main kernel Makefile targets to generate a perf sources tarball -# suitable for build outside the full kernel sources. -# -# This is to test that the tools/perf/MANIFEST file lists all the files needed to -# be in such tarball, which sometimes gets broken when we move files around, -# like when we made some files that were in tools/perf/ available to other tools/ -# codebases by moving it to tools/include/, etc. - -PERF=$1 -cd ${PERF}/../..tools/perf/tests/perf-targz-src-pkg line 21
Quoted 10 lines
tools/perf/tests/perf-targz-src-pkg deleted +0
-10-make perf-targz-src-pkg > /dev/null -TARBALL=$(ls -rt perf-*.tar.gz) -TMP_DEST=$(mktemp -d) -tar xf ${TARBALL} -C $TMP_DEST -rm -f ${TARBALL} -cd - > /dev/null -make -C $TMP_DEST/perf*/tools/perf > /dev/null 2>&1 -RC=$? -rm -rf ${TMP_DEST} -exit $RCSNIP
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Namhyung Kim
maintainerPATCH 12/40 perf tools: Use thread__comm_by_time() when adding hist entries
In reply to Namhyung KimNow thread->comm can be handled with time properly, use it to find correct comm when adding hist entries.
--- tools/perf/builtin-annotate.c | 5 +++-- tools/perf/builtin-diff.c | 8 ++++---- tools/perf/tests/hists_link.c | 4 ++-- tools/perf/util/hist.c | 19 ++++++++++--------- tools/perf/util/hist.h | 2 +- 5 files changed, 20 insertions(+), 18 deletions(-)
tools/perf/builtin-annotate.c +3 -2
@@ -47,7 +47,7 @@ struct perf_annotate { };static int perf_evsel__add_sample (struct perf_evsel *evsel, -struct perf_sample *sample__maybe_unused , +struct perf_sample *sample,struct addr_location *al,struct perf_annotate *ann) {
@@ -67,7 +67,8 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,return 0 ; } - he =__hists__add_entry (hists, al,NULL ,NULL ,NULL ,1 ,1 ,0 , true); + he =__hists__add_entry (hists, al,NULL ,NULL ,NULL ,1 ,1 ,0 , + sample->time, true);if (he ==NULL )return -ENOMEM ;
tools/perf/builtin-diff.c +4 -4
@@ -312,10 +312,10 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,static int hists__add_entry (struct hists *hists,struct addr_location *al,u64 period, -u64 weight,u64 transaction) +u64 weight,u64 transaction,u64 timestamp) {if (__hists__add_entry (hists, al,NULL ,NULL ,NULL , period, weight, - transaction, true) !=NULL ) + transaction, timestamp, true) !=NULL )return 0 ;return -ENOMEM ; }
@@ -336,8 +336,8 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,return -1 ; } -if (hists__add_entry (hists, &al, sample->period, - sample->weight, sample->transaction)) { +if (hists__add_entry (hists, &al, sample->period, sample->weight, + sample->transaction, sample->time)) {pr_warning ("problem incrementing symbol period, skipping event\n" ); goto out_put; }
tools/perf/tests/hists_link.c +2 -2
@@ -90,7 +90,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) goto out; he =__hists__add_entry (hists, &al,NULL , -NULL ,NULL ,1 ,1 ,0 , true); +NULL ,NULL ,1 ,1 ,0 ,-1 , true);if (he ==NULL ) {addr_location__put (&al); goto out;
@@ -116,7 +116,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) goto out; he =__hists__add_entry (hists, &al,NULL , -NULL ,NULL ,1 ,1 ,0 , true); +NULL ,NULL ,1 ,1 ,0 ,-1 , true);if (he ==NULL ) {addr_location__put (&al); goto out;
tools/perf/util/hist.c +10 -9
@@ -442,11 +442,11 @@ struct hist_entry *__hists__add_entry(struct hists *hists, struct branch_info *bi, struct mem_info *mi, u64 period, u64 weight, u64 transaction, - bool sample_self) + u64 timestamp, bool sample_self) { struct hist_entry entry = { .thread = al->thread, - .comm = thread__comm(al->thread), + .comm = thread__comm_by_time(al->thread, timestamp), .ms = { .map = al->map, .sym = al->sym,
@@ -504,13 +504,14 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al {u64 cost;struct mem_info *mi = iter->priv; +struct perf_sample *sample = iter->sample;struct hists *hists =evsel__hists (iter->evsel);struct hist_entry *he;if (mi ==NULL )return -EINVAL ; - cost = iter->sample->weight; + cost = sample->weight;if (!cost) cost =1 ;
@@ -522,7 +523,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al* and the he_stat__add_period() function. */ he =__hists__add_entry (hists, al, iter->parent,NULL , mi, - cost, cost,0 , true); + cost, cost,0 , sample->time, true);if (!he)return -ENOMEM ;
@@ -623,7 +624,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a* and not events sampled. Thus we use a pseudo period of 1. */ he =__hists__add_entry (hists, al, iter->parent, &bi[i],NULL , -1 ,1 ,0 , true); +1 ,1 ,0 , iter->sample->time, true);if (he ==NULL )return -ENOMEM ;
@@ -661,7 +662,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location he =__hists__add_entry (evsel__hists (evsel), al, iter->parent,NULL ,NULL , sample->period, sample->weight, - sample->transaction, true); + sample->transaction, sample->time, true);if (he ==NULL )return -ENOMEM ;
@@ -723,7 +724,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, he =__hists__add_entry (hists, al, iter->parent,NULL ,NULL , sample->period, sample->weight, - sample->transaction, true); + sample->transaction, sample->time, true);if (he ==NULL )return -ENOMEM ;
@@ -767,7 +768,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,struct hist_entry he_tmp = { .cpu = al->cpu, .thread = al->thread, - .comm =thread__comm (al->thread), + .comm =thread__comm_by_time (al->thread, sample->time), .ip = al->addr, .ms = { .map = al->map,
@@ -796,7 +797,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, he =__hists__add_entry (evsel__hists (evsel), al, iter->parent,NULL ,NULL , sample->period, sample->weight, - sample->transaction, false); + sample->transaction, sample->time, false);if (he ==NULL )return -ENOMEM ;
tools/perf/util/hist.h +1 -1
@@ -109,7 +109,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,struct branch_info *bi, struct mem_info *mi, u64 period, u64 weight, u64 transaction, - bool sample_self); + u64 timestamp, bool sample_self);int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al,struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg);
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 13/40 perf tools: Convert dead thread list into rbtree
In reply to Namhyung KimCurrently perf maintains dead threads in a linked list but this can be a problem if someone needs to search from it especially in a large session which might have many dead threads. Convert it to a rbtree like normal threads and it'll be used later with multi-file changes.
The list node is now used for chaining dead threads of same tid since it's easier to handle such threads in time order.
--- tools/perf/util/machine.c | 80 ++++++++++++++++++++++++++++++++++++++++++----- tools/perf/util/machine.h | 2 +- tools/perf/util/thread.c | 21 +++++++++++-- tools/perf/util/thread.h | 11 +++---- 4 files changed, 96 insertions(+), 18 deletions(-)
tools/perf/util/machine.c +72 -8
@@ -30,8 +30,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)dsos__init (&machine->kernel_dsos); machine->threads =RB_ROOT ; + machine->dead_threads =RB_ROOT ;pthread_rwlock_init (&machine->threads_lock,NULL ); -INIT_LIST_HEAD (&machine->dead_threads); machine->last_match =NULL ; machine->vdso_info =NULL ;
@@ -93,6 +93,29 @@ static void dsos__delete(struct dsos *dsos) } } +static void machine__delete_dead_threads (struct machine *machine) +{ +struct rb_node *nd =rb_first (&machine->dead_threads); + +while (nd) { + struct thread *t = rb_entry(nd, struct thread, rb_node); +struct thread *pos; + + nd =rb_next (nd); +rb_erase (&t->rb_node, &machine->dead_threads); +RB_CLEAR_NODE (&t->rb_node); + +while (!list_empty (&t->tid_node)) { + pos =list_first_entry (&t->tid_node, + struct thread, tid_node); +list_del_init (&pos->tid_node); +thread__delete (pos); + } + +thread__delete (t); + } +} +void machine__delete_threads (struct machine *machine) {struct rb_node *nd;
@@ -106,6 +129,8 @@ void machine__delete_threads(struct machine *machine)__machine__remove_thread (machine, t, false); }pthread_rwlock_unlock (&machine->threads_lock); + +machine__delete_dead_threads (machine); }void machine__exit (struct machine *machine)
@@ -1308,6 +1333,10 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *eventstatic void __machine__remove_thread (struct machine *machine,struct thread *th,bool lock) { +struct rb_node **p = &machine->dead_threads.rb_node; +struct rb_node *parent =NULL ; +struct thread *pos; +if (machine->last_match == th) machine->last_match =NULL ;
@@ -1316,15 +1345,43 @@ static void __machine__remove_thread(struct machine *machine, struct thread *th,pthread_rwlock_wrlock (&machine->threads_lock);rb_erase (&th->rb_node, &machine->threads);RB_CLEAR_NODE (&th->rb_node); + + th->dead = true; + +/* +* No need to have an additional reference for non-index file. +*/ +if (!perf_has_index) { +thread__put (th); + goto out; + } +/* -* Move it first to the dead_threads list, then drop the reference, -* if this is the last reference, then the thread__delete destructor -* will be called and we will remove it from the dead_threads list. +* For indexed file, We may have references to this (dead) +* thread, as samples are processed after fork/exit events. +* Just move them to a separate rbtree. */ -list_add_tail (&th->node, &machine->dead_threads); +while (*p !=NULL ) { + parent = *p; + pos = rb_entry(parent, struct thread, rb_node); + +if (pos->tid == th->tid) { +list_add_tail (&th->tid_node, &pos->tid_node); + goto out; + } + +if (th->tid < pos->tid) + p = &(*p)->rb_left; +else + p = &(*p)->rb_right; + } + +rb_link_node (&th->rb_node, parent, p); +rb_insert_color (&th->rb_node, &machine->dead_threads); + +out:if (lock)pthread_rwlock_unlock (&machine->threads_lock); -thread__put (th); }void machine__remove_thread (struct machine *machine,struct thread *th)
@@ -1826,7 +1883,7 @@ int machine__for_each_thread(struct machine *machine, void *priv) {struct rb_node *nd; -struct thread *thread; +struct thread *thread, *pos;int rc =0 ;for (nd =rb_first (&machine->threads); nd; nd =rb_next (nd)) {
@@ -1836,10 +1893,17 @@ int machine__for_each_thread(struct machine *machine,return rc; } -list_for_each_entry (thread, &machine->dead_threads, node) { +for (nd =rb_first (&machine->dead_threads); nd; nd =rb_next (nd)) { + thread = rb_entry(nd, struct thread, rb_node); rc =fn (thread, priv);if (rc !=0 )return rc; + +list_for_each_entry (pos, &thread->tid_node, tid_node) { + rc =fn (pos, priv); +if (rc !=0 ) +return rc; + } }return rc; }
tools/perf/util/machine.h +1 -1
@@ -30,8 +30,8 @@ struct machine {bool comm_exec;char *root_dir;struct rb_root threads; +struct rb_root dead_threads;pthread_rwlock_t threads_lock; -struct list_head dead_threads;struct thread *last_match;struct vdso_info *vdso_info;struct dsos user_dsos;
tools/perf/util/thread.c +18 -3
@@ -9,6 +9,7 @@#include "debug.h" #include "comm.h" #include "unwind.h" +#include "machine.h" int thread__init_map_groups (struct thread *thread,struct machine *machine) {
@@ -38,6 +39,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->ppid =-1 ; thread->cpu =-1 ;INIT_LIST_HEAD (&thread->comm_list); +INIT_LIST_HEAD (&thread->tid_node);if (unwind__prepare_access (thread) <0 ) goto err_thread;
@@ -54,7 +56,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)list_add (&comm->list, &thread->comm_list);atomic_set (&thread->refcnt,0 ); -INIT_LIST_HEAD (&thread->node); +INIT_LIST_HEAD (&thread->tid_node);RB_CLEAR_NODE (&thread->rb_node); }
@@ -70,7 +72,7 @@ void thread__delete(struct thread *thread)struct comm *comm, *tmp;BUG_ON (!RB_EMPTY_NODE (&thread->rb_node)); -BUG_ON (!list_empty (&thread->node)); +BUG_ON (!list_empty (&thread->tid_node));thread_stack__free (thread);
@@ -97,7 +99,20 @@ struct thread *thread__get(struct thread *thread)void thread__put (struct thread *thread) {if (thread &&atomic_dec_and_test (&thread->refcnt)) { -list_del_init (&thread->node); +if (!RB_EMPTY_NODE (&thread->rb_node)) { +struct machine *machine = thread->mg->machine; + +if (thread->dead) { +rb_erase (&thread->rb_node, + &machine->dead_threads); + }else { +rb_erase (&thread->rb_node, + &machine->threads); + } +RB_CLEAR_NODE (&thread->rb_node); + } + +list_del_init (&thread->tid_node);thread__delete (thread); } }
tools/perf/util/thread.h +5 -6
@@ -13,10 +13,8 @@struct thread_stack ;struct thread { -union { -struct rb_node rb_node; -struct list_head node; - }; +struct rb_node rb_node; +struct list_head tid_node;struct map_groups *mg;pid_t pid_;/* Not all tools update this */ pid_t tid;
@@ -25,7 +23,8 @@ struct thread {atomic_t refcnt;char shortname[3 ];bool comm_set; -bool dead;/* if set thread has exited */ +bool exited;/* if set thread has exited */ +bool dead;/* thread is in dead_threads list */ struct list_head comm_list;int comm_len;u64 db_id;
@@ -54,7 +53,7 @@ static inline void __thread__zput(struct thread **thread)static inline void thread__exited (struct thread *thread) { - thread->dead = true; + thread->exited = true; } int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp,
Signature · 1 line
2.4.0
Re: [PATCH 13/40] perf tools: Convert dead thread list into rbtree
On Mon, May 18, 2015 at 09:30:28AM +0900, Namhyung Kim wrote:
SNIP
Quoted 9 lines
tools/perf/util/thread.c +1
-0@@ -38,6 +39,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->ppid =-1 ; thread->cpu =-1 ;INIT_LIST_HEAD (&thread->comm_list); +INIT_LIST_HEAD (&thread->tid_node);if (unwind__prepare_access (thread) <0 ) goto err_thread;
tools/perf/util/thread.c struct thread *thread__new(pid_t pid, pid_t tid) · line 59
Quoted 5 lines
tools/perf/util/thread.c +1
-1@@ -54,7 +56,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)list_add (&comm->list, &thread->comm_list);atomic_set (&thread->refcnt,0 ); -INIT_LIST_HEAD (&thread->node); +INIT_LIST_HEAD (&thread->tid_node);
tid_node is initialized twice
jirka
Namhyung Kim
maintainerRe: [PATCH 13/40] perf tools: Convert dead thread list into rbtree
Quoted 14 lines · Jiri Olsa
On Mon, May 18, 2015 at 09:30:28AM +0900, Namhyung Kim wrote:
SNIP
Quoted 10 lines
tools/perf/util/thread.c +1
-0@@ -38,6 +39,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->ppid =-1 ; thread->cpu =-1 ;INIT_LIST_HEAD (&thread->comm_list); +INIT_LIST_HEAD (&thread->tid_node);if (unwind__prepare_access (thread) <0 ) goto err_thread;@@ -54,7 +56,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
Quoted 6 lines
tools/perf/util/thread.c struct thread *thread__new(pid_t pid, pid_t tid) · line 59
Quoted 4 lines
tools/perf/util/thread.c +1
-1list_add (&comm->list, &thread->comm_list);atomic_set (&thread->refcnt,0 ); -INIT_LIST_HEAD (&thread->node); +INIT_LIST_HEAD (&thread->tid_node);
tid_nodeis initialized twice
Okay, will fix.
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 14/40 perf tools: Introduce machine__find*_thread_by_time()
In reply to Namhyung KimWith data file indexing is enabled, it needs to search thread based on sample time since sample processing is done after other (task, comm and mmap) events are processed. This can be a problem if a session is very long and pid is recycled - in that case it'll only see the last one.
So keep thread start time in it, and search thread based on the time. This patch introduces machine__find{,new}_thread_by_time() function for this. It'll first search current thread rbtree and then dead thread tree and list. If it couldn't find anyone, it'll create a new thread.
The sample timestamp of 0 means that this is called from synthesized event so just use current rbtree. The timestamp will be -1 if sample didn't record the timestamp so will see current threads automatically.
--- tools/perf/tests/dwarf-unwind.c | 8 +-- tools/perf/tests/hists_common.c | 3 +- tools/perf/tests/hists_link.c | 2 +- tools/perf/util/event.c | 6 +- tools/perf/util/machine.c | 126 +++++++++++++++++++++++++++++++++++++++- tools/perf/util/machine.h | 10 +++- tools/perf/util/thread.c | 4 ++ tools/perf/util/thread.h | 1 + 8 files changed, 148 insertions(+), 12 deletions(-)
tools/perf/tests/dwarf-unwind.c +3 -5
@@ -16,10 +16,10 @@static int mmap_handler (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,NULL ); +return machine__process_mmap2_event (machine, event, sample); }static int init_live_machine (struct machine *machine)
@@ -66,12 +66,10 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) __attribute__ ((noinline))static int unwind_thread (struct thread *thread) { -struct perf_sample sample; +struct perf_sample sample = { .time =-1ULL , };unsigned long cnt =0 ;int err =-1 ; -memset (&sample,0 ,sizeof (sample)); -if (test__arch_unwind_sample (&sample, thread)) {pr_debug ("failed to get unwind sample\n" ); goto out;
tools/perf/tests/hists_common.c +2 -1
@@ -80,6 +80,7 @@ static struct {struct machine *setup_fake_machine (struct machines *machines) {struct machine *machine =machines__find (machines,HOST_KERNEL_ID ); +struct perf_sample sample = { .time =-1ULL , };size_t i;if (machine ==NULL ) {
@@ -114,7 +115,7 @@ 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,NULL ); +machine__process_mmap_event (machine, &fake_mmap_event, &sample); }for (i =0 ; i <ARRAY_SIZE (fake_symbols); i++) {
tools/perf/tests/hists_link.c +1 -1
@@ -64,7 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)struct perf_evsel *evsel;struct addr_location al;struct hist_entry *he; -struct perf_sample sample = { .period =1 , }; +struct perf_sample sample = { .period =1 , .time =-1ULL , };size_t i =0 , k;/*
tools/perf/util/event.c +4 -2
@@ -9,6 +9,7 @@#include "strlist.h" #include "thread.h" #include "thread_map.h" +#include "session.h" #include "symbol/kallsyms.h" static const char *perf_event__names[] = {
@@ -929,9 +930,10 @@ int perf_event__preprocess_sample(const union perf_event *event, struct perf_sample *sample) {u8 cpumode = event->header.misc &PERF_RECORD_MISC_CPUMODE_MASK ; -struct thread *thread =machine__findnew_thread (machine, sample->pid, - sample->tid); +struct thread *thread; + thread =machine__findnew_thread_by_time (machine, sample->pid, + sample->tid, sample->time);if (thread ==NULL )return -1 ;
tools/perf/util/machine.c +125 -1
144 lines not on this page · raw message
tools/perf/util/machine.h +8 -2
@@ -69,8 +69,6 @@ static inline bool machine__kernel_ip(struct machine *machine, u64 ip)return ip >= kernel_start; } -struct thread *machine__find_thread (struct machine *machine,pid_t pid, -pid_t tid);struct comm *machine__thread_exec_comm (struct machine *machine,struct thread *thread);
@@ -154,6 +152,14 @@ static inline bool machine__is_host(struct machine *machine)struct thread *__machine__findnew_thread (struct machine *machine,pid_t pid,pid_t tid);struct thread *machine__findnew_thread (struct machine *machine,pid_t pid,pid_t tid); +struct thread *machine__find_thread (struct machine *machine,pid_t pid, +pid_t tid); +struct thread *machine__findnew_thread_by_time (struct machine *machine, +pid_t pid,pid_t tid, +u64 timestamp); +struct thread *machine__find_thread_by_time (struct machine *machine, +pid_t pid,pid_t tid, +u64 timestamp);size_t machine__fprintf (struct machine *machine,FILE *fp);
tools/perf/util/thread.c +4 -0
@@ -161,6 +161,9 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,/* Override the default :tid entry */ if (!thread->comm_set) { +if (!thread->start_time) + thread->start_time = timestamp; + err =comm__override (curr, str, timestamp, exec);if (err)return err;
@@ -261,6 +264,7 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) } thread->ppid = parent->tid; + thread->start_time = timestamp;return thread__clone_map_groups (thread, parent); }
tools/perf/util/thread.h +1 -0
@@ -28,6 +28,7 @@ struct thread {struct list_head comm_list;int comm_len;u64 db_id; +u64 start_time;void *priv;struct thread_stack *ts;
Signature · 1 line
2.4.0
Re: [PATCH 14/40] perf tools: Introduce machine__find*_thread_by_time()
On Mon, May 18, 2015 at 09:30:29AM +0900, Namhyung Kim wrote:
SNIP
Quoted 20 lines
tools/perf/util/machine.c +20
-0+static struct thread * +__machine__findnew_thread_by_time (struct machine *machine,pid_t pid,pid_t tid, +u64 timestamp,bool create) +{ +struct thread *curr, *pos, *new; +struct thread *th =NULL ; +struct rb_node **p; +struct rb_node *parent =NULL ; + +if (!perf_has_index) +return ____machine__findnew_thread (machine, pid, tid, create); + +/* lookup current thread first */ + curr =____machine__findnew_thread (machine, pid, tid, false); +if (curr && timestamp >= curr->start_time) +return curr; + +/* and then check dead threads tree & list */ + p = &machine->dead_threads.rb_node; +while (*p !=NULL ) {
tools/perf/util/machine.c struct thread *machine__find_thread(struct machine *machine, pid_t pid, · line 498
Quoted 10 lines
tools/perf/util/machine.c +10
-0+ parent = *p; + th = rb_entry(parent, struct thread, rb_node); + +if (th->tid == tid) { +list_for_each_entry (pos, &th->tid_node, tid_node) { +if (timestamp >= pos->start_time && + pos->start_time > th->start_time) { + th = pos; +break ; + }
hum, how do we know, there's not another thread on the list fitting the timestamp >= pos->start_time condition as well?
jirka
Re: [PATCH 14/40] perf tools: Introduce machine__find*_thread_by_time()
Quoted 29 lines · Jiri Olsa
On Mon, May 18, 2015 at 09:30:29AM +0900, Namhyung Kim wrote:
SNIP
Quoted 25 lines
tools/perf/util/machine.c +25
-0+static struct thread * +__machine__findnew_thread_by_time (struct machine *machine,pid_t pid,pid_t tid, +u64 timestamp,bool create) +{ +struct thread *curr, *pos, *new; +struct thread *th =NULL ; +struct rb_node **p; +struct rb_node *parent =NULL ; + +if (!perf_has_index) +return ____machine__findnew_thread (machine, pid, tid, create); + +/* lookup current thread first */ + curr =____machine__findnew_thread (machine, pid, tid, false); +if (curr && timestamp >= curr->start_time) +return curr; + +/* and then check dead threads tree & list */ + p = &machine->dead_threads.rb_node; +while (*p !=NULL ) { + parent = *p; + th = rb_entry(parent, struct thread, rb_node); + +if (th->tid == tid) { +list_for_each_entry (pos, &th->tid_node, tid_node) {
Quoted 8 lines
tools/perf/util/machine.c struct thread *machine__find_thread(struct machine *machine, pid_t pid, · line 498
Quoted 5 lines
tools/perf/util/machine.c +5
-0+if (timestamp >= pos->start_time && + pos->start_time > th->start_time) { + th = pos; +break ; + }hum, how do we know, there's not another thread on the list fitting the timestamp >=
pos->start_timecondition as well?
should we store exit_time for dead threads?
jirka
Namhyung Kim
maintainerRe: [PATCH 14/40] perf tools: Introduce machine__find*_thread_by_time()
In reply to Jiri OlsaQuoted 29 lines · Jiri Olsa
On Mon, May 18, 2015 at 09:30:29AM +0900, Namhyung Kim wrote:
SNIP
Quoted 25 lines
tools/perf/util/machine.c +25
-0+static struct thread * +__machine__findnew_thread_by_time (struct machine *machine,pid_t pid,pid_t tid, +u64 timestamp,bool create) +{ +struct thread *curr, *pos, *new; +struct thread *th =NULL ; +struct rb_node **p; +struct rb_node *parent =NULL ; + +if (!perf_has_index) +return ____machine__findnew_thread (machine, pid, tid, create); + +/* lookup current thread first */ + curr =____machine__findnew_thread (machine, pid, tid, false); +if (curr && timestamp >= curr->start_time) +return curr; + +/* and then check dead threads tree & list */ + p = &machine->dead_threads.rb_node; +while (*p !=NULL ) { + parent = *p; + th = rb_entry(parent, struct thread, rb_node); + +if (th->tid == tid) { +list_for_each_entry (pos, &th->tid_node, tid_node) {
Quoted 8 lines
tools/perf/util/machine.c struct thread *machine__find_thread(struct machine *machine, pid_t pid, · line 498
Quoted 5 lines
tools/perf/util/machine.c +5
-0+if (timestamp >= pos->start_time && + pos->start_time > th->start_time) { + th = pos; +break ; + }hum, how do we know, there's not another thread on the list fitting the timestamp >=
pos->start_timecondition as well?
Because the list is sorted on time - it'll see thread that has a larger start_time first so it can ensure that a thread which satisfies the expression first is the current thread at that time. Later threads in the list will satisfy the expression also but they're already dead.
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 15/40 perf tools: Add a test case for timed thread handling
In reply to Namhyung KimA test case for verifying live and dead thread tree management during time change and new machine__find{,new}_thread_time().
--- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 + tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-lookup-time.c | 179 ++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+) create mode 100644 tools/perf/tests/thread-lookup-time.c
tools/perf/tests/Build +1 -0
@@ -26,6 +26,7 @@ perf-y += sw-clock.o perf-y += mmap-thread-lookup.o perf-y += thread-comm.o perf-y += thread-mg-share.o +perf-y += thread-lookup-time.o perf-y += switch-tracking.o perf-y += keep-tracking.o perf-y += code-reading.o
tools/perf/tests/builtin-test.c +4 -0
@@ -175,6 +175,10 @@ static struct test { .func = test__thread_comm, }, { + .desc = "Test thread lookup with time", + .func = test__thread_lookup_time, + }, + { .func = NULL, }, };
tools/perf/tests/tests.h +1 -0
@@ -62,6 +62,7 @@ int test__fdarray__filter(void);int test__fdarray__add (void );int test__kmod_path__parse (void );int test__thread_comm (void ); +int test__thread_lookup_time (void );#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)#ifdef HAVE_DWARF_UNWIND_SUPPORT
tools/perf/tests/thread-lookup-time.c new file +179 -0
179 lines not on this page · raw message
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 16/40 perf tools: Reducing arguments of hist_entry_iter__add()
In reply to Namhyung KimThe evsel and sample arguments are to set iter for later use. As it also receives an iter as another argument, just set them before calling the function.
--- tools/perf/builtin-report.c | 9 +++++---- tools/perf/builtin-top.c | 7 ++++--- tools/perf/tests/hists_cumulate.c | 6 ++++-- tools/perf/tests/hists_filter.c | 4 +++- tools/perf/tests/hists_output.c | 6 ++++-- tools/perf/util/hist.c | 8 ++------ tools/perf/util/hist.h | 1 - 7 files changed, 22 insertions(+), 19 deletions(-)
tools/perf/builtin-report.c +5 -4
@@ -139,8 +139,10 @@ static int process_sample_event(struct perf_tool *tool, struct report *rep = container_of(tool, struct report, tool);struct addr_location al;struct hist_entry_iter iter = { - .hide_unresolved = rep->hide_unresolved, - .add_entry_cb = hist_iter__report_callback, + .evsel = evsel, + .sample = sample, + .hide_unresolved = rep->hide_unresolved, + .add_entry_cb = hist_iter__report_callback, };int ret =0 ;
@@ -168,8 +170,7 @@ static int process_sample_event(struct perf_tool *tool,if (al.map !=NULL ) al.map->dso->hit =1 ; - ret =hist_entry_iter__add (&iter, &al, evsel, sample, rep->max_stack, - rep); + ret =hist_entry_iter__add (&iter, &al, rep->max_stack, rep);if (ret <0 )pr_debug ("problem adding hist entry, skipping event\n" ); out_put:
tools/perf/builtin-top.c +4 -3
@@ -775,7 +775,9 @@ static void perf_event__process_sample(struct perf_tool *tool,if (al.sym ==NULL || !al.sym->ignore) {struct hists *hists =evsel__hists (evsel);struct hist_entry_iter iter = { - .add_entry_cb = hist_iter__top_callback, + .evsel = evsel, + .sample = sample, + .add_entry_cb = hist_iter__top_callback, };if (symbol_conf.cumulate_callchain)
@@ -785,8 +787,7 @@ static void perf_event__process_sample(struct perf_tool *tool,pthread_mutex_lock (&hists->lock); - err =hist_entry_iter__add (&iter, &al, evsel, sample, - top->max_stack, top); + err =hist_entry_iter__add (&iter, &al, top->max_stack, top);if (err <0 )pr_err ("Problem incrementing symbol period, skipping event\n" );
tools/perf/tests/hists_cumulate.c +4 -2
@@ -87,6 +87,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .hide_unresolved = false, };
@@ -104,8 +106,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }
tools/perf/tests/hists_filter.c +3 -1
@@ -63,6 +63,8 @@ static int add_hist_entries(struct perf_evlist *evlist, }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };
@@ -81,7 +83,7 @@ static int add_hist_entries(struct perf_evlist *evlist, &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH ,NULL ) <0 ) {addr_location__put (&al); goto out;
tools/perf/tests/hists_output.c +4 -2
@@ -57,6 +57,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };
@@ -70,8 +72,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }
tools/perf/util/hist.c +2 -6
@@ -852,19 +852,15 @@ const struct hist_iter_ops hist_iter_cumulative = { };int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg) {int err, err2; - err =sample__resolve_callchain (sample, &iter->parent, evsel, al, - max_stack_depth); + err =sample__resolve_callchain (iter->sample, &iter->parent, + iter->evsel, al, max_stack_depth);if (err)return err; - iter->evsel = evsel; - iter->sample = sample; - err = iter->ops->prepare_entry (iter, al);if (err) goto out;
tools/perf/util/hist.h +0 -1
@@ -111,7 +111,6 @@ struct hist_entry *__hists__add_entry(struct hists *hists, u64 weight, u64 transaction, u64 timestamp, bool sample_self);int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg);int64_t hist_entry__cmp (struct hist_entry *left,struct hist_entry *right);
Signature · 1 line
2.4.0
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 16/40] perf tools: Reducing arguments of hist_entry_iter__add()
Em Mon, May 18, 2015 at 09:30:31AM +0900, Namhyung Kim escreveu:
Quoted 3 lines
The evsel and sample arguments are to set iter for later use. As it also receives an iter as another argument, just set them before calling the function.
This looks like can be applied sooner, tried but it didn't apply :-\
- Arnaldo
Quoted 173 lines
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/builtin-report.c | 9 +++++---- tools/perf/builtin-top.c | 7 ++++--- tools/perf/tests/hists_cumulate.c | 6 ++++-- tools/perf/tests/hists_filter.c | 4 +++- tools/perf/tests/hists_output.c | 6 ++++-- tools/perf/util/hist.c | 8 ++------ tools/perf/util/hist.h | 1 - 7 files changed, 22 insertions(+), 19 deletions(-)tools/perf/builtin-report.c +5
-4@@ -139,8 +139,10 @@ static int process_sample_event(struct perf_tool *tool, struct report *rep = container_of(tool, struct report, tool);struct addr_location al;struct hist_entry_iter iter = { - .hide_unresolved = rep->hide_unresolved, - .add_entry_cb = hist_iter__report_callback, + .evsel = evsel, + .sample = sample, + .hide_unresolved = rep->hide_unresolved, + .add_entry_cb = hist_iter__report_callback, };int ret =0 ;@@ -168,8 +170,7 @@ static int process_sample_event(struct perf_tool *tool,if (al.map !=NULL ) al.map->dso->hit =1 ; - ret =hist_entry_iter__add (&iter, &al, evsel, sample, rep->max_stack, - rep); + ret =hist_entry_iter__add (&iter, &al, rep->max_stack, rep);if (ret <0 )pr_debug ("problem adding hist entry, skipping event\n" ); out_put:tools/perf/builtin-top.c +4
-3@@ -775,7 +775,9 @@ static void perf_event__process_sample(struct perf_tool *tool,if (al.sym ==NULL || !al.sym->ignore) {struct hists *hists =evsel__hists (evsel);struct hist_entry_iter iter = { - .add_entry_cb = hist_iter__top_callback, + .evsel = evsel, + .sample = sample, + .add_entry_cb = hist_iter__top_callback, };if (symbol_conf.cumulate_callchain)@@ -785,8 +787,7 @@ static void perf_event__process_sample(struct perf_tool *tool,pthread_mutex_lock (&hists->lock); - err =hist_entry_iter__add (&iter, &al, evsel, sample, - top->max_stack, top); + err =hist_entry_iter__add (&iter, &al, top->max_stack, top);if (err <0 )pr_err ("Problem incrementing symbol period, skipping event\n" );tools/perf/tests/hists_cumulate.c +4
-2@@ -87,6 +87,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .hide_unresolved = false, };@@ -104,8 +106,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }tools/perf/tests/hists_filter.c +3
-1@@ -63,6 +63,8 @@ static int add_hist_entries(struct perf_evlist *evlist, }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };@@ -81,7 +83,7 @@ static int add_hist_entries(struct perf_evlist *evlist, &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH ,NULL ) <0 ) {addr_location__put (&al); goto out;tools/perf/tests/hists_output.c +4
-2@@ -57,6 +57,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };@@ -70,8 +72,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }tools/perf/util/hist.c +2
-6@@ -852,19 +852,15 @@ const struct hist_iter_ops hist_iter_cumulative = { };int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg) {int err, err2; - err =sample__resolve_callchain (sample, &iter->parent, evsel, al, - max_stack_depth); + err =sample__resolve_callchain (iter->sample, &iter->parent, + iter->evsel, al, max_stack_depth);if (err)return err; - iter->evsel = evsel; - iter->sample = sample; - err = iter->ops->prepare_entry (iter, al);if (err) goto out;tools/perf/util/hist.h +0
-1@@ -111,7 +111,6 @@ struct hist_entry *__hists__add_entry(struct hists *hists, u64 weight, u64 transaction, u64 timestamp, bool sample_self);int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg);int64_t hist_entry__cmp (struct hist_entry *left,struct hist_entry *right);Signature · 1 line
2.4.0
Namhyung Kim
maintainerRe: [PATCH 16/40] perf tools: Reducing arguments of hist_entry_iter__add()
Quoted 6 lines · Arnaldo Carvalho de Melo
Em Mon, May 18, 2015 at 09:30:31AM +0900, Namhyung Kim escreveu:
Quoted 3 lines
The evsel and sample arguments are to set iter for later use. As it also receives an iter as another argument, just set them before calling the function.
This looks like can be applied sooner, tried but it didn't apply :-\
I'll send it as a separate patch.
Thanks,
Namhyung
Quoted 181 lines
- Arnaldo
Quoted 173 lines
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/builtin-report.c | 9 +++++---- tools/perf/builtin-top.c | 7 ++++--- tools/perf/tests/hists_cumulate.c | 6 ++++-- tools/perf/tests/hists_filter.c | 4 +++- tools/perf/tests/hists_output.c | 6 ++++-- tools/perf/util/hist.c | 8 ++------ tools/perf/util/hist.h | 1 - 7 files changed, 22 insertions(+), 19 deletions(-)tools/perf/builtin-report.c +5
-4@@ -139,8 +139,10 @@ static int process_sample_event(struct perf_tool *tool, struct report *rep = container_of(tool, struct report, tool);struct addr_location al;struct hist_entry_iter iter = { - .hide_unresolved = rep->hide_unresolved, - .add_entry_cb = hist_iter__report_callback, + .evsel = evsel, + .sample = sample, + .hide_unresolved = rep->hide_unresolved, + .add_entry_cb = hist_iter__report_callback, };int ret =0 ;@@ -168,8 +170,7 @@ static int process_sample_event(struct perf_tool *tool,if (al.map !=NULL ) al.map->dso->hit =1 ; - ret =hist_entry_iter__add (&iter, &al, evsel, sample, rep->max_stack, - rep); + ret =hist_entry_iter__add (&iter, &al, rep->max_stack, rep);if (ret <0 )pr_debug ("problem adding hist entry, skipping event\n" ); out_put:tools/perf/builtin-top.c +4
-3@@ -775,7 +775,9 @@ static void perf_event__process_sample(struct perf_tool *tool,if (al.sym ==NULL || !al.sym->ignore) {struct hists *hists =evsel__hists (evsel);struct hist_entry_iter iter = { - .add_entry_cb = hist_iter__top_callback, + .evsel = evsel, + .sample = sample, + .add_entry_cb = hist_iter__top_callback, };if (symbol_conf.cumulate_callchain)@@ -785,8 +787,7 @@ static void perf_event__process_sample(struct perf_tool *tool,pthread_mutex_lock (&hists->lock); - err =hist_entry_iter__add (&iter, &al, evsel, sample, - top->max_stack, top); + err =hist_entry_iter__add (&iter, &al, top->max_stack, top);if (err <0 )pr_err ("Problem incrementing symbol period, skipping event\n" );tools/perf/tests/hists_cumulate.c +4
-2@@ -87,6 +87,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .hide_unresolved = false, };@@ -104,8 +106,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }tools/perf/tests/hists_filter.c +3
-1@@ -63,6 +63,8 @@ static int add_hist_entries(struct perf_evlist *evlist, }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };@@ -81,7 +83,7 @@ static int add_hist_entries(struct perf_evlist *evlist, &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH ,NULL ) <0 ) {addr_location__put (&al); goto out;tools/perf/tests/hists_output.c +4
-2@@ -57,6 +57,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };@@ -70,8 +72,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }tools/perf/util/hist.c +2
-6@@ -852,19 +852,15 @@ const struct hist_iter_ops hist_iter_cumulative = { };int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg) {int err, err2; - err =sample__resolve_callchain (sample, &iter->parent, evsel, al, - max_stack_depth); + err =sample__resolve_callchain (iter->sample, &iter->parent, + iter->evsel, al, max_stack_depth);if (err)return err; - iter->evsel = evsel; - iter->sample = sample; - err = iter->ops->prepare_entry (iter, al);if (err) goto out;tools/perf/util/hist.h +0
-1@@ -111,7 +111,6 @@ struct hist_entry *__hists__add_entry(struct hists *hists, u64 weight, u64 transaction, u64 timestamp, bool sample_self);int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg);int64_t hist_entry__cmp (struct hist_entry *left,struct hist_entry *right);Signature · 1 line
2.4.0--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Namhyung Kim
maintainerPATCH 1/1 perf tools: Reducing arguments of hist_entry_iter__add()
The evsel and sample arguments are to set iter for later use. As it also receives an iter as another argument, just set them before calling the function.
---
Arnaldo, it seems that this patch can be applied to the current perf/core 2d8e405acd78 ("perf bench numa: Share sched_getcpu() __weak def with cloexec.c") cleanly. If you see any conflict, please let me know.
tools/perf/builtin-report.c | 9 +++++---- tools/perf/builtin-top.c | 7 ++++--- tools/perf/tests/hists_cumulate.c | 6 ++++-- tools/perf/tests/hists_filter.c | 4 +++- tools/perf/tests/hists_output.c | 6 ++++-- tools/perf/util/hist.c | 8 ++------ tools/perf/util/hist.h | 1 - 7 files changed, 22 insertions(+), 19 deletions(-)
tools/perf/builtin-report.c +5 -4
@@ -139,8 +139,10 @@ static int process_sample_event(struct perf_tool *tool, struct report *rep = container_of(tool, struct report, tool);struct addr_location al;struct hist_entry_iter iter = { - .hide_unresolved = rep->hide_unresolved, - .add_entry_cb = hist_iter__report_callback, + .evsel = evsel, + .sample = sample, + .hide_unresolved = rep->hide_unresolved, + .add_entry_cb = hist_iter__report_callback, };int ret =0 ;
@@ -168,8 +170,7 @@ static int process_sample_event(struct perf_tool *tool,if (al.map !=NULL ) al.map->dso->hit =1 ; - ret =hist_entry_iter__add (&iter, &al, evsel, sample, rep->max_stack, - rep); + ret =hist_entry_iter__add (&iter, &al, rep->max_stack, rep);if (ret <0 )pr_debug ("problem adding hist entry, skipping event\n" ); out_put:
tools/perf/builtin-top.c +4 -3
@@ -775,7 +775,9 @@ static void perf_event__process_sample(struct perf_tool *tool,if (al.sym ==NULL || !al.sym->ignore) {struct hists *hists =evsel__hists (evsel);struct hist_entry_iter iter = { - .add_entry_cb = hist_iter__top_callback, + .evsel = evsel, + .sample = sample, + .add_entry_cb = hist_iter__top_callback, };if (symbol_conf.cumulate_callchain)
@@ -785,8 +787,7 @@ static void perf_event__process_sample(struct perf_tool *tool,pthread_mutex_lock (&hists->lock); - err =hist_entry_iter__add (&iter, &al, evsel, sample, - top->max_stack, top); + err =hist_entry_iter__add (&iter, &al, top->max_stack, top);if (err <0 )pr_err ("Problem incrementing symbol period, skipping event\n" );
tools/perf/tests/hists_cumulate.c +4 -2
@@ -87,6 +87,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .hide_unresolved = false, };
@@ -104,8 +106,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }
tools/perf/tests/hists_filter.c +3 -1
@@ -63,6 +63,8 @@ static int add_hist_entries(struct perf_evlist *evlist, }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };
@@ -81,7 +83,7 @@ static int add_hist_entries(struct perf_evlist *evlist, &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH ,NULL ) <0 ) {addr_location__put (&al); goto out;
tools/perf/tests/hists_output.c +4 -2
@@ -57,6 +57,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };
@@ -70,8 +72,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }
tools/perf/util/hist.c +2 -6
@@ -851,19 +851,15 @@ const struct hist_iter_ops hist_iter_cumulative = { };int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg) {int err, err2; - err =sample__resolve_callchain (sample, &iter->parent, evsel, al, - max_stack_depth); + err =sample__resolve_callchain (iter->sample, &iter->parent, + iter->evsel, al, max_stack_depth);if (err)return err; - iter->evsel = evsel; - iter->sample = sample; - err = iter->ops->prepare_entry (iter, al);if (err) goto out;
tools/perf/util/hist.h +0 -1
@@ -111,7 +111,6 @@ struct hist_entry *__hists__add_entry(struct hists *hists, u64 weight, u64 transaction, bool sample_self);int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg);int64_t hist_entry__cmp (struct hist_entry *left,struct hist_entry *right);
Signature · 1 line
2.4.0
Re: [PATCH] perf tools: Reducing arguments of hist_entry_iter__add()
Em Tue, May 19, 2015 at 05:04:10PM +0900, Namhyung Kim escreveu:
Quoted 9 lines
The evsel and sample arguments are to set iter for later use. As it also receives an iter as another argument, just set them before calling the function.
Signed-off-by Namhyung Kim <[email protected]>---
Arnaldo, it seems that this patch can be applied to the current perf/core2d8e405acd78("perf bench numa: Sharesched_getcpu()__weakdef withcloexec.c") cleanly. If you see any conflict, please let me know.
Thanks, applied.
- Arnaldo
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf hists: Reducing arguments of hist_entry_iter__add()
In reply to Namhyung Kimperf hists: Reducing arguments of hist_entry_iter__add()
Commit-ID: 063bd9363bb8979b2939bdc0412d98a8ac062e3b Gitweb: http://git.kernel.org/tip/063bd9363bb8979b2939bdc0412d98a8ac062e3b Author: Namhyung Kim <[email protected]> AuthorDate: Tue, 19 May 2015 17:04:10 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Wed, 27 May 2015 12:21:43 -0300
perf hists: Reducing arguments of hist_entry_iter__add()
The evsel and sample arguments are to set iter for later use. As it also receives an iter as another argument, just set them before calling the function.
--- tools/perf/builtin-report.c | 9 +++++---- tools/perf/builtin-top.c | 7 ++++--- tools/perf/tests/hists_cumulate.c | 6 ++++-- tools/perf/tests/hists_filter.c | 4 +++- tools/perf/tests/hists_output.c | 6 ++++-- tools/perf/util/hist.c | 8 ++------ tools/perf/util/hist.h | 1 - 7 files changed, 22 insertions(+), 19 deletions(-)
tools/perf/builtin-report.c +5 -4
@@ -139,8 +139,10 @@ static int process_sample_event(struct perf_tool *tool, struct report *rep = container_of(tool, struct report, tool);struct addr_location al;struct hist_entry_iter iter = { - .hide_unresolved = rep->hide_unresolved, - .add_entry_cb = hist_iter__report_callback, + .evsel = evsel, + .sample = sample, + .hide_unresolved = rep->hide_unresolved, + .add_entry_cb = hist_iter__report_callback, };int ret =0 ;
@@ -168,8 +170,7 @@ static int process_sample_event(struct perf_tool *tool,if (al.map !=NULL ) al.map->dso->hit =1 ; - ret =hist_entry_iter__add (&iter, &al, evsel, sample, rep->max_stack, - rep); + ret =hist_entry_iter__add (&iter, &al, rep->max_stack, rep);if (ret <0 )pr_debug ("problem adding hist entry, skipping event\n" ); out_put:
tools/perf/builtin-top.c +4 -3
@@ -775,7 +775,9 @@ static void perf_event__process_sample(struct perf_tool *tool,if (al.sym ==NULL || !al.sym->ignore) {struct hists *hists =evsel__hists (evsel);struct hist_entry_iter iter = { - .add_entry_cb = hist_iter__top_callback, + .evsel = evsel, + .sample = sample, + .add_entry_cb = hist_iter__top_callback, };if (symbol_conf.cumulate_callchain)
@@ -785,8 +787,7 @@ static void perf_event__process_sample(struct perf_tool *tool,pthread_mutex_lock (&hists->lock); - err =hist_entry_iter__add (&iter, &al, evsel, sample, - top->max_stack, top); + err =hist_entry_iter__add (&iter, &al, top->max_stack, top);if (err <0 )pr_err ("Problem incrementing symbol period, skipping event\n" );
tools/perf/tests/hists_cumulate.c +4 -2
@@ -87,6 +87,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .hide_unresolved = false, };
@@ -104,8 +106,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }
tools/perf/tests/hists_filter.c +3 -1
@@ -63,6 +63,8 @@ static int add_hist_entries(struct perf_evlist *evlist, }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };
@@ -81,7 +83,7 @@ static int add_hist_entries(struct perf_evlist *evlist, &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH ,NULL ) <0 ) {addr_location__put (&al); goto out;
tools/perf/tests/hists_output.c +4 -2
@@ -57,6 +57,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) }, };struct hist_entry_iter iter = { + .evsel = evsel, + .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false, };
@@ -70,8 +72,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; -if (hist_entry_iter__add (&iter, &al, evsel, &sample, -PERF_MAX_STACK_DEPTH ,NULL ) <0 ) { +if (hist_entry_iter__add (&iter, &al,PERF_MAX_STACK_DEPTH , +NULL ) <0 ) {addr_location__put (&al); goto out; }
tools/perf/util/hist.c +2 -6
@@ -851,19 +851,15 @@ const struct hist_iter_ops hist_iter_cumulative = { };int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg) {int err, err2; - err =sample__resolve_callchain (sample, &iter->parent, evsel, al, - max_stack_depth); + err =sample__resolve_callchain (iter->sample, &iter->parent, + iter->evsel, al, max_stack_depth);if (err)return err; - iter->evsel = evsel; - iter->sample = sample; - err = iter->ops->prepare_entry (iter, al);if (err) goto out;
tools/perf/util/hist.h +0 -1
@@ -111,7 +111,6 @@ struct hist_entry *__hists__add_entry(struct hists *hists, u64 weight, u64 transaction, bool sample_self);int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al, -struct perf_evsel *evsel,struct perf_sample *sample,int max_stack_depth,void *arg);int64_t hist_entry__cmp (struct hist_entry *left,struct hist_entry *right);
Namhyung Kim
maintainerPATCH 17/40 perf tools: Maintain map groups list in a leader thread
In reply to Namhyung KimTo support multi-threaded perf report, we need to maintain time-sorted map groups. Add ->mg_list member to struct thread and sort the list by time. Now leader threads have one more refcnt for map groups in the list so also update the thread-mg-share test case.
Currently only add a new map groups when an exec (comm) event is received.
--- tools/perf/tests/thread-mg-share.c | 7 ++- tools/perf/util/event.c | 2 + tools/perf/util/machine.c | 4 +- tools/perf/util/map.c | 3 ++ tools/perf/util/map.h | 2 + tools/perf/util/thread.c | 108 ++++++++++++++++++++++++++++++++++++- tools/perf/util/thread.h | 3 ++ 7 files changed, 124 insertions(+), 5 deletions(-)
tools/perf/tests/thread-mg-share.c +5 -2
@@ -23,6 +23,9 @@ int test__thread_mg_share(void)* with several threads and checks they properly share and * maintain map groups info (struct map_groups). * +* Note that a leader thread has one more refcnt for its +* (current) map groups. +* * thread group (pid: 0, tids: 0, 1, 2, 3) * other group (pid: 4, tids: 4, 5) */
@@ -43,7 +46,7 @@ int test__thread_mg_share(void) leader && t1 && t2 && t3 && other); mg = leader->mg; -TEST_ASSERT_EQUAL ("wrong refcnt" , mg->refcnt,4 ); +TEST_ASSERT_EQUAL ("wrong refcnt" , mg->refcnt,5 );/* test the map groups pointer is shared */ TEST_ASSERT_VAL ("map groups don't match" , mg == t1->mg);
@@ -71,7 +74,7 @@ int test__thread_mg_share(void)machine__remove_thread (machine, other_leader); other_mg = other->mg; -TEST_ASSERT_EQUAL ("wrong refcnt" , other_mg->refcnt,2 ); +TEST_ASSERT_EQUAL ("wrong refcnt" , other_mg->refcnt,3 );TEST_ASSERT_VAL ("map groups don't match" , other_mg == other_leader->mg);
tools/perf/util/event.c +2 -0
@@ -851,6 +851,8 @@ void thread__find_addr_map(struct thread *thread, u8 cpumode,return ; } +BUG_ON (mg ==NULL ); +if (cpumode ==PERF_RECORD_MISC_KERNEL && perf_host) { al->level ='k' ; mg = &machine->kmaps;
tools/perf/util/machine.c +2 -2
@@ -341,7 +341,7 @@ static void machine__update_thread_pid(struct machine *machine, goto out_err;if (!leader->mg) - leader->mg =map_groups__new (machine); +thread__set_map_groups (leader,map_groups__new (machine),0 );if (!leader->mg) goto out_err;
@@ -358,7 +358,7 @@ static void machine__update_thread_pid(struct machine *machine,if (!map_groups__empty (th->mg))pr_err ("Discarding thread maps for %d:%d\n" , th->pid_, th->tid); -map_groups__delete (th->mg); +map_groups__put (th->mg); } th->mg =map_groups__get (leader->mg);
tools/perf/util/map.c +3 -0
@@ -427,6 +427,8 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) } mg->machine = machine; mg->refcnt =1 ; + mg->timestamp =0 ; +INIT_LIST_HEAD (&mg->list); }static void maps__delete (struct rb_root *maps)
@@ -489,6 +491,7 @@ struct map_groups *map_groups__new(struct machine *machine)void map_groups__delete (struct map_groups *mg) {map_groups__exit (mg); +list_del (&mg->list);free (mg); }
tools/perf/util/map.h +2 -0
@@ -61,7 +61,9 @@ struct map_groups {struct rb_root maps[MAP__NR_TYPES ];struct list_head removed_maps[MAP__NR_TYPES ];struct machine *machine; +u64 timestamp;int refcnt; +struct list_head list; };struct map_groups *map_groups__new (struct machine *machine);
tools/perf/util/thread.c +107 -1
150 lines not on this page · raw message
tools/perf/util/thread.h +3 -0
@@ -16,6 +16,7 @@ struct thread {struct rb_node rb_node;struct list_head tid_node;struct map_groups *mg; +struct list_head mg_list;pid_t pid_;/* Not all tools update this */ pid_t tid;pid_t ppid;
@@ -71,6 +72,8 @@ struct comm *thread__exec_comm(const struct thread *thread);struct comm *thread__comm_by_time (const struct thread *thread,u64 timestamp);const char *thread__comm_str (const struct thread *thread);const char *thread__comm_str_by_time (const struct thread *thread,u64 timestamp); +struct map_groups *thread__get_map_groups (struct thread *thread,u64 timestamp); +int thread__set_map_groups (struct thread *thread,struct map_groups *mg,u64 timestamp);void thread__insert_map (struct thread *thread,struct map *map);int thread__fork (struct thread *thread,struct thread *parent,u64 timestamp);size_t thread__fprintf (struct thread *thread,FILE *fp);
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 18/40 perf tools: Introduce thread__find_addr_location_by_time() and friends
In reply to Namhyung KimThese new functions are for find appropriate map (and symbol) at the given time when used with an indexed data file. This is based on the fact that map_groups list is sorted by time in the previous patch.
--- tools/perf/util/event.c | 59 +++++++++++++++++++++++++++++++++++++++-------- tools/perf/util/machine.c | 28 ++++++++++++++-------- tools/perf/util/session.h | 1 + tools/perf/util/thread.c | 26 +++++++++++++++++++++ tools/perf/util/thread.h | 11 +++++++++ 5 files changed, 106 insertions(+), 19 deletions(-)
tools/perf/util/event.c +50 -9
@@ -832,16 +832,14 @@ int perf_event__process(struct perf_tool *tool __maybe_unused,return machine__process_event (machine, event, sample); } -void thread__find_addr_map (struct thread *thread,u8 cpumode, -enum map_type type,u64 addr, -struct addr_location *al) +static void map_groups__find_addr_map (struct map_groups *mg,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al) { -struct map_groups *mg = thread->mg;struct machine *machine = mg->machine;bool load_map = false; al->machine = machine; - al->thread = thread; al->addr = addr; al->cpumode = cpumode; al->filtered =0 ;
@@ -910,6 +908,29 @@ void thread__find_addr_map(struct thread *thread, u8 cpumode, } } +void thread__find_addr_map (struct thread *thread,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al) +{ + al->thread = thread; +map_groups__find_addr_map (thread->mg, cpumode, type, addr, al); +} + +void thread__find_addr_map_by_time (struct thread *thread,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al,u64 timestamp) +{ +struct map_groups *mg; + +if (perf_has_index) + mg =thread__get_map_groups (thread, timestamp); +else + mg = thread->mg; + + al->thread = thread; +map_groups__find_addr_map (mg, cpumode, type, addr, al); +} +void thread__find_addr_location (struct thread *thread,u8 cpumode,enum map_type type,u64 addr,struct addr_location *al)
@@ -922,6 +943,23 @@ void thread__find_addr_location(struct thread *thread, al->sym =NULL ; } +void thread__find_addr_location_by_time (struct thread *thread,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al,u64 timestamp) +{ +if (perf_has_index) +thread__find_addr_map_by_time (thread, cpumode, type, addr, al, + timestamp); +else +thread__find_addr_map (thread, cpumode, type, addr, al); + +if (al->map !=NULL ) + al->sym =map__find_symbol (al->map, al->addr, + thread->mg->machine->symbol_filter); +else + al->sym =NULL ; +} +/* * Callers need to drop the reference to al->thread, obtained in * machine__findnew_thread()
@@ -951,7 +989,9 @@ int perf_event__preprocess_sample(const union perf_event *event, machine->vmlinux_maps[MAP__FUNCTION] == NULL)machine__create_kernel_maps (machine); -thread__find_addr_map (thread, cpumode,MAP__FUNCTION , sample->ip, al); +thread__find_addr_map_by_time (thread, cpumode,MAP__FUNCTION , + sample->ip, al, sample->time); +dump_printf (" ...... dso: %s\n" , al->map ? al->map->dso->long_name : al->level =='H' ?"[hypervisor]" :"<not found>" );
@@ -1026,10 +1066,11 @@ void perf_event__preprocess_sample_addr(union perf_event *event, {u8 cpumode = event->header.misc &PERF_RECORD_MISC_CPUMODE_MASK ; -thread__find_addr_map (thread, cpumode,MAP__FUNCTION , sample->addr, al); +thread__find_addr_map_by_time (thread, cpumode,MAP__FUNCTION , + sample->addr, al, sample->time);if (!al->map) -thread__find_addr_map (thread, cpumode,MAP__VARIABLE , - sample->addr, al); +thread__find_addr_map_by_time (thread, cpumode,MAP__VARIABLE , + sample->addr, al, sample->time); al->cpu = sample->cpu; al->sym =NULL ;
tools/perf/util/machine.c +18 -10
@@ -1604,7 +1604,7 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex)static void ip__resolve_ams (struct thread *thread,struct addr_map_symbol *ams, -u64 ip) +u64 ip,u64 timestamp) {struct addr_location al;
@@ -1616,7 +1616,8 @@ static void ip__resolve_ams(struct thread *thread,* Thus, we have to try consecutively until we find a match * or else, the symbol is unknown */ -thread__find_cpumode_addr_location (thread,MAP__FUNCTION , ip, &al); +thread__find_cpumode_addr_location_by_time (thread,MAP__FUNCTION , + ip, &al, timestamp); ams->addr = ip; ams->al_addr = al.addr;
@@ -1624,21 +1625,25 @@ static void ip__resolve_ams(struct thread *thread, ams->map = al.map; } -static void ip__resolve_data (struct thread *thread, -u8 m,struct addr_map_symbol *ams,u64 addr) +static void ip__resolve_data (struct thread *thread,u8 m, +struct addr_map_symbol *ams, +u64 addr,u64 timestamp) {struct addr_location al;memset (&al,0 ,sizeof (al)); -thread__find_addr_location (thread, m,MAP__VARIABLE , addr, &al); +thread__find_addr_location_by_time (thread, m,MAP__VARIABLE , + addr, &al, timestamp); +if (al.map ==NULL ) {/* * some shared data regions have execute bit set which puts * their mapping in the MAP__FUNCTION type array. * Check there as a fallback option before dropping the sample. */ -thread__find_addr_location (thread, m,MAP__FUNCTION , addr, &al); +thread__find_addr_location_by_time (thread, m,MAP__FUNCTION , + addr, &al, timestamp); } ams->addr = addr;
@@ -1655,8 +1660,9 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,if (!mi)return NULL ; -ip__resolve_ams (al->thread, &mi->iaddr, sample->ip); -ip__resolve_data (al->thread, al->cpumode, &mi->daddr, sample->addr); +ip__resolve_ams (al->thread, &mi->iaddr, sample->ip, sample->time); +ip__resolve_data (al->thread, al->cpumode, &mi->daddr, sample->addr, + sample->time); mi->data_src.val = sample->data_src;return mi;
@@ -1730,8 +1736,10 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,return NULL ;for (i =0 ; i < bs->nr; i++) { -ip__resolve_ams (al->thread, &bi[i].to, bs->entries[i].to); -ip__resolve_ams (al->thread, &bi[i].from, bs->entries[i].from); +ip__resolve_ams (al->thread, &bi[i].to, + bs->entries[i].to, sample->time); +ip__resolve_ams (al->thread, &bi[i].from, + bs->entries[i].from, sample->time); bi[i].flags = bs->entries[i].flags; }return bi;
tools/perf/util/session.h +1 -0
@@ -136,4 +136,5 @@ int perf_event__synthesize_id_index(struct perf_tool *tool,struct perf_evlist *evlist, struct machine *machine); +#endif /* __PERF_SESSION_H */
tools/perf/util/thread.c +26 -0
@@ -392,3 +392,29 @@ void thread__find_cpumode_addr_location(struct thread *thread,break ; } } + +void thread__find_cpumode_addr_location_by_time (struct thread *thread, +enum map_type type,u64 addr, +struct addr_location *al, +u64 timestamp) +{ +size_t i; +const u8 const cpumodes[] = { +PERF_RECORD_MISC_USER , +PERF_RECORD_MISC_KERNEL , +PERF_RECORD_MISC_GUEST_USER , +PERF_RECORD_MISC_GUEST_KERNEL + }; + +if (!perf_has_index) { +thread__find_cpumode_addr_location (thread, type, addr, al); +return ; + } + +for (i =0 ; i <ARRAY_SIZE (cpumodes); i++) { +thread__find_addr_location_by_time (thread, cpumodes[i], type, + addr, al, timestamp); +if (al->map) +break ; + } +}
tools/perf/util/thread.h +11 -0
@@ -81,14 +81,25 @@ size_t thread__fprintf(struct thread *thread, FILE *fp);void thread__find_addr_map (struct thread *thread,u8 cpumode,enum map_type type,u64 addr,struct addr_location *al); +void thread__find_addr_map_by_time (struct thread *thread,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al,u64 timestamp);void thread__find_addr_location (struct thread *thread,u8 cpumode,enum map_type type,u64 addr,struct addr_location *al); +void thread__find_addr_location_by_time (struct thread *thread,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al, +u64 timestamp);void thread__find_cpumode_addr_location (struct thread *thread,enum map_type type,u64 addr,struct addr_location *al); +void thread__find_cpumode_addr_location_by_time (struct thread *thread, +enum map_type type,u64 addr, +struct addr_location *al, +u64 timestamp);static inline void *thread__priv (struct thread *thread) {
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 19/40 perf callchain: Use thread__find_addr_location_by_time() and friends
In reply to Namhyung KimFind correct thread/map/symbol using proper functions.
--- tools/perf/util/machine.c | 25 ++++++++++++-------- .../util/scripting-engines/trace-event-python.c | 4 ++-- tools/perf/util/session.h | 1 - tools/perf/util/unwind-libdw.c | 12 ++++++---- tools/perf/util/unwind-libunwind.c | 27 +++++++++++----------- 5 files changed, 39 insertions(+), 30 deletions(-)
tools/perf/util/machine.c +16 -9
@@ -1672,15 +1672,17 @@ static int add_callchain_ip(struct thread *thread,struct symbol **parent, struct addr_location *root_al, u8 *cpumode, - u64 ip) + u64 ip, + u64 timestamp) { struct addr_location al; al.filtered =0 ; al.sym =NULL ;if (!cpumode) { -thread__find_cpumode_addr_location (thread,MAP__FUNCTION , - ip, &al); +thread__find_cpumode_addr_location_by_time (thread, +MAP__FUNCTION , ip, + &al, timestamp); }else {if (ip >=PERF_CONTEXT_MAX ) {switch (ip) {
@@ -1705,8 +1707,9 @@ static int add_callchain_ip(struct thread *thread, }return 0 ; } -thread__find_addr_location (thread, *cpumode,MAP__FUNCTION , - ip, &al); +thread__find_addr_location_by_time (thread, *cpumode, +MAP__FUNCTION , ip, + &al, timestamp); }if (al.sym !=NULL ) {
@@ -1848,7 +1851,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread, ip = lbr_stack->entries[0 ].to; } - err =add_callchain_ip (thread, parent, root_al, &cpumode, ip); + err =add_callchain_ip (thread, parent, root_al, &cpumode, ip, + sample->time);if (err)return (err <0 ) ? err :0 ; }
@@ -1869,6 +1873,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,struct ip_callchain *chain = sample->callchain;int chain_nr =min (max_stack, (int )chain->nr);u8 cpumode =PERF_RECORD_MISC_USER ; +u64 timestamp = sample->time;int i, j, err;int skip_idx =-1 ;int first_call =0 ;
@@ -1934,10 +1939,11 @@ static int thread__resolve_callchain_sample(struct thread *thread,for (i =0 ; i < nr; i++) { err =add_callchain_ip (thread, parent, root_al, -NULL , be[i].to); +NULL , be[i].to, timestamp);if (!err) err =add_callchain_ip (thread, parent, root_al, -NULL , be[i].from); +NULL , be[i].from, + timestamp);if (err == -EINVAL )break ;if (err)
@@ -1966,7 +1972,8 @@ static int thread__resolve_callchain_sample(struct thread *thread,#endif ip = chain->ips[j]; - err =add_callchain_ip (thread, parent, root_al, &cpumode, ip); + err =add_callchain_ip (thread, parent, root_al, &cpumode, ip, + timestamp);if (err)return (err <0 ) ? err :0 ;
tools/perf/util/scripting-engines/trace-event-python.c +2 -2
@@ -304,8 +304,8 @@ static PyObject *get_field_numeric_entry(struct event_format *event,static PyObject *python_process_callchain (struct perf_sample *sample, -struct perf_evsel *evsel, -struct addr_location *al) +struct perf_evsel *evsel, +struct addr_location *al) {PyObject *pylist;
tools/perf/util/session.h +0 -1
@@ -136,5 +136,4 @@ int perf_event__synthesize_id_index(struct perf_tool *tool,struct perf_evlist *evlist, struct machine *machine); -#endif /* __PERF_SESSION_H */
tools/perf/util/unwind-libdw.c +7 -5
@@ -26,9 +26,9 @@ static int __report_module(struct addr_location *al, u64 ip,Dwfl_Module *mod;struct dso *dso =NULL ; -thread__find_addr_location (ui->thread, -PERF_RECORD_MISC_USER , -MAP__FUNCTION , ip, al); +thread__find_addr_location_by_time (ui->thread,PERF_RECORD_MISC_USER , +MAP__FUNCTION , ip, al, + ui->sample->time);if (al->map) dso = al->map->dso;
@@ -89,8 +89,10 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,struct addr_location al;ssize_t size; -thread__find_addr_map (ui->thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , addr, &al); +thread__find_addr_map_by_time (ui->thread,PERF_RECORD_MISC_USER , +MAP__FUNCTION , addr, &al, + ui->sample->time); +if (!al.map) {pr_debug ("unwind: no map for %lx\n" , (unsigned long )addr);return -1 ;
tools/perf/util/unwind-libunwind.c +14 -13
@@ -315,8 +315,10 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui) {struct addr_location al; -thread__find_addr_map (ui->thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , ip, &al); +thread__find_addr_map_by_time (ui->thread,PERF_RECORD_MISC_USER , +MAP__FUNCTION , ip, &al, + ui->sample->time); +return al.map; }
@@ -406,20 +408,19 @@ get_proc_name(unw_addr_space_t __maybe_unused as,static int access_dso_mem (struct unwind_info *ui,unw_word_t addr,unw_word_t *data) { -struct addr_location al; +struct map *map;ssize_t size; -thread__find_addr_map (ui->thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , addr, &al); -if (!al.map) { + map =find_map (addr, ui); +if (!map) {pr_debug ("unwind: no map for %lx\n" , (unsigned long )addr);return -1 ; } -if (!al.map->dso) +if (!map->dso)return -1 ; - size =dso__data_read_addr (al.map->dso, al.map, ui->machine, + size =dso__data_read_addr (map->dso, map, ui->machine, addr, (u8 *) data,sizeof (*data));return !(size ==sizeof (*data));
@@ -511,14 +512,14 @@ static void put_unwind_info(unw_addr_space_t __maybe_unused as,pr_debug ("unwind: put_unwind_info called\n" ); } -static int entry (u64 ip,struct thread *thread, +static int entry (u64 ip,struct thread *thread,u64 timestamp,unwind_entry_cb_t cb,void *arg) {struct unwind_entry e;struct addr_location al; -thread__find_addr_location (thread,PERF_RECORD_MISC_USER , -MAP__FUNCTION , ip, &al); +thread__find_addr_location_by_time (thread,PERF_RECORD_MISC_USER , +MAP__FUNCTION , ip, &al, timestamp); e.ip = ip; e.map = al.map;
@@ -620,7 +621,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,unw_word_t ip;unw_get_reg (&c,UNW_REG_IP , &ip); - ret = ip ?entry (ip, ui->thread, cb, arg) :0 ; + ret = ip ?entry (ip, ui->thread, ui->sample->time, cb, arg) :0 ; }return ret;
@@ -645,7 +646,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,if (ret)return ret; - ret =entry (ip, thread, cb, arg); + ret =entry (ip, thread, data->time, cb, arg);if (ret)return -ENOMEM ;
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 20/40 perf tools: Add a test case for timed map groups handling
In reply to Namhyung KimA test case for verifying thread->mg and ->mg_list handling during time change and new thread__find_addr_map_by_time() and friends.
--- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 ++ tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-mg-time.c | 93 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 tools/perf/tests/thread-mg-time.c
tools/perf/tests/Build +1 -0
@@ -27,6 +27,7 @@ perf-y += mmap-thread-lookup.o perf-y += thread-comm.o perf-y += thread-mg-share.o perf-y += thread-lookup-time.o +perf-y += thread-mg-time.o perf-y += switch-tracking.o perf-y += keep-tracking.o perf-y += code-reading.o
tools/perf/tests/builtin-test.c +4 -0
@@ -179,6 +179,10 @@ static struct test { .func = test__thread_lookup_time, }, { + .desc = "Test thread map group handling with time", + .func = test__thread_mg_time, + }, + { .func = NULL, }, };
tools/perf/tests/tests.h +1 -0
@@ -63,6 +63,7 @@ int test__fdarray__add(void);int test__kmod_path__parse (void );int test__thread_comm (void );int test__thread_lookup_time (void ); +int test__thread_mg_time (void );#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)#ifdef HAVE_DWARF_UNWIND_SUPPORT
tools/perf/tests/thread-mg-time.c new file +93 -0
93 lines not on this page · raw message
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 21/40 perf tools: Save timestamp of a map creation
In reply to Namhyung KimIt'll be used to support multiple maps on a same address like dlopen() and/or JIT compile cases.
--- tools/perf/util/dso.c | 2 +- tools/perf/util/machine.c | 28 ++++++++++++++++------------ tools/perf/util/machine.h | 2 +- tools/perf/util/map.c | 12 +++++++----- tools/perf/util/map.h | 9 ++++++--- tools/perf/util/probe-event.c | 2 +- tools/perf/util/symbol-elf.c | 2 +- tools/perf/util/symbol.c | 4 ++-- 8 files changed, 35 insertions(+), 26 deletions(-)
tools/perf/util/dso.c +1 -1
@@ -747,7 +747,7 @@ struct map *dso__new_map(const char *name)struct dso *dso =dso__new (name);if (dso) - map =map__new2 (0 , dso,MAP__FUNCTION ); + map =map__new2 (0 , dso,MAP__FUNCTION ,0 );return map; }
tools/perf/util/machine.c +16 -12
@@ -668,7 +668,7 @@ int machine__process_itrace_start_event(struct machine *machine __maybe_unused, }struct map *machine__new_module (struct machine *machine,u64 start, -const char *filename) +const char *filename,u64 timestamp) {struct map *map =NULL ;struct dso *dso;
@@ -686,7 +686,7 @@ struct map *machine__new_module(struct machine *machine, u64 start,if (dso ==NULL ) goto out; - map =map__new2 (start, dso,MAP__FUNCTION ); + map =map__new2 (start, dso,MAP__FUNCTION , timestamp);if (map ==NULL ) goto out;
@@ -854,7 +854,7 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)for (type =0 ; type <MAP__NR_TYPES ; ++type) {struct kmap *kmap; - machine->vmlinux_maps[type] =map__new2 (start, kernel, type); + machine->vmlinux_maps[type] =map__new2 (start, kernel, type,0 );if (machine->vmlinux_maps[type] ==NULL )return -1 ;
@@ -1155,7 +1155,7 @@ static int machine__create_module(void *arg, const char *name, u64 start)struct machine *machine = arg;struct map *map; - map =machine__new_module (machine, start, name); + map =machine__new_module (machine, start, name,0 );if (map ==NULL )return -1 ;
@@ -1256,7 +1256,8 @@ static bool machine__uses_kcore(struct machine *machine) }static int machine__process_kernel_mmap_event (struct machine *machine, -union perf_event *event) +union perf_event *event, +u64 timestamp) {struct map *map;char kmmap_prefix[PATH_MAX ];
@@ -1279,7 +1280,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,if (event->mmap.filename[0 ] =='/' || (!is_kernel_mmap && event->mmap.filename[0 ] =='[' )) { map =machine__new_module (machine, event->mmap.start, - event->mmap.filename); + event->mmap.filename, timestamp);if (map ==NULL ) goto out_problem;
@@ -1343,7 +1344,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,int machine__process_mmap2_event (struct machine *machine,union perf_event *event, -struct perf_sample *sample__maybe_unused ) +struct perf_sample *sample) {u8 cpumode = event->header.misc &PERF_RECORD_MISC_CPUMODE_MASK ;struct thread *thread;
@@ -1356,7 +1357,8 @@ int machine__process_mmap2_event(struct machine *machine,if (cpumode ==PERF_RECORD_MISC_GUEST_KERNEL || cpumode ==PERF_RECORD_MISC_KERNEL ) { - ret =machine__process_kernel_mmap_event (machine, event); + ret =machine__process_kernel_mmap_event (machine, event, + sample->time);if (ret <0 ) goto out_problem;return 0 ;
@@ -1379,7 +1381,8 @@ int machine__process_mmap2_event(struct machine *machine, event->mmap2.ino_generation, event->mmap2.prot, event->mmap2.flags, - event->mmap2.filename, type, thread); + event->mmap2.filename, type, thread, + sample->time);if (map ==NULL ) goto out_problem_map;
@@ -1396,7 +1399,7 @@ int machine__process_mmap2_event(struct machine *machine, }int machine__process_mmap_event (struct machine *machine,union perf_event *event, -struct perf_sample *sample__maybe_unused ) +struct perf_sample *sample) {u8 cpumode = event->header.misc &PERF_RECORD_MISC_CPUMODE_MASK ;struct thread *thread;
@@ -1409,7 +1412,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *eventif (cpumode ==PERF_RECORD_MISC_GUEST_KERNEL || cpumode ==PERF_RECORD_MISC_KERNEL ) { - ret =machine__process_kernel_mmap_event (machine, event); + ret =machine__process_kernel_mmap_event (machine, event, + sample->time);if (ret <0 ) goto out_problem;return 0 ;
@@ -1429,7 +1433,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event event->mmap.len, event->mmap.pgoff, event->mmap.pid,0 ,0 ,0 ,0 ,0 ,0 , event->mmap.filename, - type, thread); + type, thread, sample->time);if (map ==NULL ) goto out_problem_map;
tools/perf/util/machine.h +1 -1
@@ -193,7 +193,7 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *machine, }struct map *machine__new_module (struct machine *machine,u64 start, -const char *filename); +const char *filename,u64 timestamp);int machine__load_kallsyms (struct machine *machine,const char *filename,enum map_type type,symbol_filter_t filter);
tools/perf/util/map.c +7 -5
@@ -123,7 +123,7 @@ static inline bool replace_android_lib(const char *filename, char *newfilename) }void map__init (struct map *map,enum map_type type, -u64 start,u64 end,u64 pgoff,struct dso *dso) +u64 start,u64 end,u64 pgoff,struct dso *dso,u64 timestamp) { map->type = type; map->start = start;
@@ -137,12 +137,13 @@ void map__init(struct map *map, enum map_type type, map->groups =NULL ; map->referenced = false; map->erange_warned = false; + map->timestamp = timestamp; }struct map *map__new (struct machine *machine,u64 start,u64 len,u64 pgoff,u32 pid,u32 d_maj,u32 d_min,u64 ino,u64 ino_gen,u32 prot,u32 flags,char *filename, -enum map_type type,struct thread *thread) +enum map_type type,struct thread *thread,u64 timestamp) {struct map *map =malloc (sizeof (*map));
@@ -182,7 +183,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,if (dso ==NULL ) goto out_delete; -map__init (map, type, start, start + len, pgoff, dso); +map__init (map, type, start, start + len, pgoff, dso, timestamp);if (anon || no_dso) { map->map_ip = map->unmap_ip = identity__map_ip;
@@ -207,7 +208,8 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,* they are loaded) and for vmlinux, where only after we load all the * symbols we'll know where it starts and ends. */ -struct map *map__new2 (u64 start,struct dso *dso,enum map_type type) +struct map *map__new2 (u64 start,struct dso *dso,enum map_type type, +u64 timestamp) {struct map *map =calloc (1 , (sizeof (*map) + (dso->kernel ?sizeof (struct kmap ) :0 )));
@@ -215,7 +217,7 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type)/* * ->end will be filled after we load all the symbols */ -map__init (map, type, start,0 ,0 , dso); +map__init (map, type, start,0 ,0 , dso, timestamp); }return map;
tools/perf/util/map.h +6 -3
@@ -42,6 +42,7 @@ struct map {u32 maj, min;/* only valid for MMAP2 record */ u64 ino;/* only valid for MMAP2 record */ u64 ino_generation;/* only valid for MMAP2 record */ +u64 timestamp;/* ip -> dso rip */ u64 (*map_ip)(struct map *,u64 );
@@ -136,12 +137,14 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);int arch__compare_symbol_names (const char *namea,const char *nameb);void map__init (struct map *map,enum map_type type, -u64 start,u64 end,u64 pgoff,struct dso *dso); +u64 start,u64 end,u64 pgoff,struct dso *dso,u64 timestamp);struct map *map__new (struct machine *machine,u64 start,u64 len,u64 pgoff,u32 pid,u32 d_maj,u32 d_min,u64 ino,u64 ino_gen,u32 prot,u32 flags, -char *filename,enum map_type type,struct thread *thread); -struct map *map__new2 (u64 start,struct dso *dso,enum map_type type); +char *filename,enum map_type type,struct thread *thread, +u64 timestamp); +struct map *map__new2 (u64 start,struct dso *dso,enum map_type type, +u64 timestamp);void map__delete (struct map *map);struct map *map__clone (struct map *map);int map__overlap (struct map *l,struct map *r);
tools/perf/util/probe-event.c +1 -1
@@ -167,7 +167,7 @@ static struct map *kernel_get_module_map(const char *module)/* A file path -- this is an offline module */ if (module &&strchr (module,'/' )) -return machine__new_module (host_machine,0 , module); +return machine__new_module (host_machine,0 , module,0 );if (!module) module ="kernel" ;
tools/perf/util/symbol-elf.c +1 -1
@@ -1012,7 +1012,7 @@ int dso__load_sym(struct dso *dso, struct map *map, curr_dso->long_name = dso->long_name; curr_dso->long_name_len = dso->long_name_len; curr_map =map__new2 (start, curr_dso, - map->type); + map->type, map->timestamp);if (curr_map ==NULL ) {dso__delete (curr_dso); goto out_elf_end;
tools/perf/util/symbol.c +2 -2
@@ -781,7 +781,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta, ndso->kernel = dso->kernel; - curr_map =map__new2 (pos->start, ndso, map->type); + curr_map =map__new2 (pos->start, ndso, map->type,0 );if (curr_map ==NULL ) {dso__delete (ndso);return -1 ;
@@ -1083,7 +1083,7 @@ static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)struct kcore_mapfn_data *md = data;struct map *map; - map =map__new2 (start, md->dso, md->type); + map =map__new2 (start, md->dso, md->type,0 );if (map ==NULL )return -ENOMEM ;
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 22/40 perf tools: Introduce map_groups__{insert,find}_by_time()
In reply to Namhyung KimIt'll manage maps using timestamp so that it can find correct map/symbol for sample at a certain time. With this API, it can maintain overlapping maps in a map_groups.
--- tools/perf/util/map.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/map.h | 25 +++++++++++++++++++++++++ 2 files changed, 77 insertions(+)
tools/perf/util/map.c +52 -0
@@ -746,6 +746,33 @@ void maps__insert(struct rb_root *maps, struct map *map)rb_insert_color (&map->rb_node, maps); } +void maps__insert_by_time (struct rb_root *maps,struct map *map) +{ +struct rb_node **p = &maps->rb_node; +struct rb_node *parent =NULL ; +const u64 ip = map->start; +const u64 timestamp = map->timestamp; +struct map *m; + +while (*p !=NULL ) { + parent = *p; + m = rb_entry(parent, struct map, rb_node); +if (ip < m->start) + p = &(*p)->rb_left; +else if (ip > m->start) + p = &(*p)->rb_right; +else if (timestamp > m->timestamp) + p = &(*p)->rb_left; +else if (timestamp < m->timestamp) + p = &(*p)->rb_right; +else +BUG_ON (1 ); + } + +rb_link_node (&map->rb_node, parent, p); +rb_insert_color (&map->rb_node, maps); +} +void maps__remove (struct rb_root *maps,struct map *map) {rb_erase (&map->rb_node, maps);
@@ -771,6 +798,31 @@ struct map *maps__find(struct rb_root *maps, u64 ip)return NULL ; } +struct map *maps__find_by_time (struct rb_root *maps,u64 ip,u64 timestamp) +{ +struct rb_node **p = &maps->rb_node; +struct rb_node *parent =NULL ; +struct map *m; +struct map *best =NULL ; + +while (*p !=NULL ) { + parent = *p; + m = rb_entry(parent, struct map, rb_node); +if (ip < m->start) + p = &(*p)->rb_left; +else if (ip >= m->end) + p = &(*p)->rb_right; +else if (timestamp >= m->timestamp) { +if (!best || best->timestamp < m->timestamp) + best = m; + p = &(*p)->rb_left; + }else + p = &(*p)->rb_right; + } + +return best; +} +struct map *maps__first (struct rb_root *maps) {struct rb_node *first =rb_first (maps);
tools/perf/util/map.h +25 -0
@@ -8,6 +8,8 @@#include <stdbool.h> #include <linux/types.h> +#include "perf.h" /* for perf_has_index */ +enum map_type {MAP__FUNCTION =0 ,MAP__VARIABLE ,
@@ -166,8 +168,10 @@ void map__reloc_vmlinux(struct map *map);size_t __map_groups__fprintf_maps (struct map_groups *mg,enum map_type type,FILE *fp);void maps__insert (struct rb_root *maps,struct map *map); +void maps__insert_by_time (struct rb_root *maps,struct map *map);void maps__remove (struct rb_root *maps,struct map *map);struct map *maps__find (struct rb_root *maps,u64 addr); +struct map *maps__find_by_time (struct rb_root *maps,u64 addr,u64 timestamp);struct map *maps__first (struct rb_root *maps);struct map *maps__next (struct map *map);void map_groups__init (struct map_groups *mg,struct machine *machine);
@@ -185,6 +189,17 @@ static inline void map_groups__insert(struct map_groups *mg, struct map *map) map->groups = mg; } +static inline void map_groups__insert_by_time (struct map_groups *mg, +struct map *map) +{ +if (perf_has_index) +maps__insert_by_time (&mg->maps[map->type], map); +else +maps__insert (&mg->maps[map->type], map); + + map->groups = mg; +} +static inline void map_groups__remove (struct map_groups *mg,struct map *map) {maps__remove (&mg->maps[map->type], map);
@@ -196,6 +211,16 @@ static inline struct map *map_groups__find(struct map_groups *mg,return maps__find (&mg->maps[type], addr); } +static inline struct map *map_groups__find_by_time (struct map_groups *mg, +enum map_type type,u64 addr, +u64 timestamp) +{ +if (!perf_has_index) +return maps__find (&mg->maps[type], addr); + +return maps__find_by_time (&mg->maps[type], addr, timestamp); +} +static inline struct map *map_groups__first (struct map_groups *mg,enum map_type type) {
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 23/40 perf tools: Use map_groups__find_addr_by_time()
In reply to Namhyung KimUse timestamp to find a corresponding map so that it can find a match symbol eventually.
--- tools/perf/util/event.c | 81 ++++++++++++++++++++++++++++++++++++++++++------ tools/perf/util/thread.c | 8 +++-- 2 files changed, 77 insertions(+), 12 deletions(-)
tools/perf/util/event.c +71 -10
@@ -832,12 +832,11 @@ int perf_event__process(struct perf_tool *tool __maybe_unused,return machine__process_event (machine, event, sample); } -static void map_groups__find_addr_map (struct map_groups *mg,u8 cpumode, -enum map_type type,u64 addr, -struct addr_location *al) +static bool map_groups__set_addr_location (struct map_groups *mg, +struct addr_location *al, +u8 cpumode,u64 addr) {struct machine *machine = mg->machine; -bool load_map = false; al->machine = machine; al->addr = addr;
@@ -846,21 +845,17 @@ static void map_groups__find_addr_map(struct map_groups *mg, u8 cpumode,if (machine ==NULL ) { al->map =NULL ; -return ; +return true; }BUG_ON (mg ==NULL );if (cpumode ==PERF_RECORD_MISC_KERNEL && perf_host) { al->level ='k' ; - mg = &machine->kmaps; - load_map = true; }else if (cpumode ==PERF_RECORD_MISC_USER && perf_host) { al->level ='.' ; }else if (cpumode ==PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { al->level ='g' ; - mg = &machine->kmaps; - load_map = true; }else if (cpumode ==PERF_RECORD_MISC_GUEST_USER && perf_guest) { al->level ='u' ; }else {
@@ -876,8 +871,27 @@ static void map_groups__find_addr_map(struct map_groups *mg, u8 cpumode, !perf_host) al->filtered |= (1 <<HIST_FILTER__HOST ); +return true; + } +return false; +} + +static void map_groups__find_addr_map (struct map_groups *mg,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al) +{ +struct machine *machine = mg->machine; +bool load_map = false; + +if (map_groups__set_addr_location (mg, al, cpumode, addr))return ; + +if ((cpumode ==PERF_RECORD_MISC_KERNEL && perf_host) || + (cpumode ==PERF_RECORD_MISC_GUEST_KERNEL && perf_guest)) { + mg = &machine->kmaps; + load_map = true; } + try_again: al->map =map_groups__find (mg, type, al->addr);if (al->map ==NULL ) {
@@ -908,6 +922,53 @@ static void map_groups__find_addr_map(struct map_groups *mg, u8 cpumode, } } +static void map_groups__find_addr_map_by_time (struct map_groups *mg,u8 cpumode, +enum map_type type,u64 addr, +struct addr_location *al, +u64 timestamp) +{ +struct machine *machine = mg->machine; +bool load_map = false; + +if (map_groups__set_addr_location (mg, al, cpumode, addr)) +return ; + +if ((cpumode ==PERF_RECORD_MISC_KERNEL && perf_host) || + (cpumode ==PERF_RECORD_MISC_GUEST_KERNEL && perf_guest)) { + mg = &machine->kmaps; + load_map = true; + } + +try_again: + al->map =map_groups__find_by_time (mg, type, al->addr, timestamp); +if (al->map ==NULL ) { +/* +* If this is outside of all known maps, and is a negative +* address, try to look it up in the kernel dso, as it might be +* a vsyscall or vdso (which executes in user-mode). +* +* XXX This is nasty, we should have a symbol list in the +* "[vdso]" dso, but for now lets use the old trick of looking +* in the whole kernel symbol list. +*/ +if (cpumode ==PERF_RECORD_MISC_USER && machine && + mg != &machine->kmaps && +machine__kernel_ip (machine, al->addr)) { + mg = &machine->kmaps; + load_map = true; + goto try_again; + } + }else { +/* +* Kernel maps might be changed when loading symbols so loading +* must be done prior to using kernel maps. +*/ +if (load_map) +map__load (al->map, machine->symbol_filter); + al->addr = al->map->map_ip (al->map, al->addr); + } +} +void thread__find_addr_map (struct thread *thread,u8 cpumode,enum map_type type,u64 addr,struct addr_location *al)
@@ -928,7 +989,7 @@ void thread__find_addr_map_by_time(struct thread *thread, u8 cpumode, mg = thread->mg; al->thread = thread; -map_groups__find_addr_map (mg, cpumode, type, addr, al); +map_groups__find_addr_map_by_time (mg, cpumode, type, addr, al, timestamp); } void thread__find_addr_location(struct thread *thread,
tools/perf/util/thread.c +6 -2
@@ -335,8 +335,12 @@ size_t thread__fprintf(struct thread *thread, FILE *fp)void thread__insert_map (struct thread *thread,struct map *map) { -map_groups__fixup_overlappings (thread->mg, map, stderr); -map_groups__insert (thread->mg, map); +if (perf_has_index) { +map_groups__insert_by_time (thread->mg, map); + }else { +map_groups__fixup_overlappings (thread->mg, map, stderr); +map_groups__insert (thread->mg, map); + } } static int thread__clone_map_groups(struct thread *thread,
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 24/40 perf tools: Add testcase for managing maps with time
In reply to Namhyung KimThis tests new map_groups__{insert,find}_by_time() API working correctly by using 3 * 100 maps.
--- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 ++ tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-map-time.c | 90 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 tools/perf/tests/thread-map-time.c
tools/perf/tests/Build +1 -0
@@ -28,6 +28,7 @@ perf-y += thread-comm.o perf-y += thread-mg-share.o perf-y += thread-lookup-time.o perf-y += thread-mg-time.o +perf-y += thread-map-time.o perf-y += switch-tracking.o perf-y += keep-tracking.o perf-y += code-reading.o
tools/perf/tests/builtin-test.c +4 -0
@@ -183,6 +183,10 @@ static struct test { .func = test__thread_mg_time, }, { + .desc = "Test thread map lookup with time", + .func = test__thread_map_lookup_time, + }, + { .func = NULL, }, };
tools/perf/tests/tests.h +1 -0
@@ -64,6 +64,7 @@ int test__kmod_path__parse(void);int test__thread_comm (void );int test__thread_lookup_time (void );int test__thread_mg_time (void ); +int test__thread_map_lookup_time (void );#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)#ifdef HAVE_DWARF_UNWIND_SUPPORT
tools/perf/tests/thread-map-time.c new file +90 -0
90 lines not on this page · raw message
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 25/40 perf tools: Protect dso symbol loading using a mutex
In reply to Namhyung KimWhen multi-thread support for perf report is enabled, it's possible to access a dso concurrently. Add a new pthread_mutex to protect it from concurrent dso__load().
--- tools/perf/util/dso.c | 2 ++ tools/perf/util/dso.h | 1 + tools/perf/util/symbol.c | 34 ++++++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 10 deletions(-)
tools/perf/util/dso.c +2 -0
@@ -936,6 +936,7 @@ struct dso *dso__new(const char *name)RB_CLEAR_NODE (&dso->rb_node);INIT_LIST_HEAD (&dso->node);INIT_LIST_HEAD (&dso->data.open_entry); +pthread_mutex_init (&dso->lock,NULL ); }return dso;
@@ -966,6 +967,7 @@ void dso__delete(struct dso *dso)dso_cache__free (&dso->data.cache);dso__free_a2l (dso);zfree (&dso->symsrc_filename); +pthread_mutex_destroy (&dso->lock);free (dso); }
tools/perf/util/dso.h +1 -0
@@ -129,6 +129,7 @@ struct dsos {struct auxtrace_cache ;struct dso { +pthread_mutex_t lock;struct list_head node;struct rb_node rb_node;/* rbtree node sorted by long name */ struct rb_root symbols[MAP__NR_TYPES ];
tools/perf/util/symbol.c +24 -10
@@ -1383,12 +1383,22 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)struct symsrc *syms_ss =NULL , *runtime_ss =NULL ;bool kmod; -dso__set_loaded (dso, map->type); +pthread_mutex_lock (&dso->lock); + +/* check again under the dso->lock */ +if (dso__loaded (dso, map->type)) { + ret =1 ; + goto out; + } + +if (dso->kernel) { +if (dso->kernel ==DSO_TYPE_KERNEL ) + ret =dso__load_kernel_sym (dso, map, filter); +else if (dso->kernel ==DSO_TYPE_GUEST_KERNEL ) + ret =dso__load_guest_kernel_sym (dso, map, filter); -if (dso->kernel ==DSO_TYPE_KERNEL ) -return dso__load_kernel_sym (dso, map, filter); -else if (dso->kernel ==DSO_TYPE_GUEST_KERNEL ) -return dso__load_guest_kernel_sym (dso, map, filter); + goto out; + }if (map->groups && map->groups->machine) machine = map->groups->machine;
@@ -1401,18 +1411,18 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)struct stat st;if (lstat (dso->name, &st) <0 ) -return -1 ; + goto out;if (st.st_uid && (st.st_uid !=geteuid ())) {pr_warning ("File %s not owned by current user or root, " "ignoring it.\n" , dso->name); -return -1 ; + goto out; } ret =dso__load_perf_map (dso, map, filter); dso->symtab_type = ret >0 ?DSO_BINARY_TYPE__JAVA_JIT :DSO_BINARY_TYPE__NOT_FOUND ; -return ret; + goto out; }if (machine)
@@ -1420,7 +1430,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) name =malloc (PATH_MAX );if (!name) -return -1 ; + goto out; kmod = dso->symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
@@ -1501,7 +1511,11 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) out_free:free (name);if (ret <0 &&strstr (dso->name," (deleted)" ) !=NULL ) -return 0 ; + ret =0 ; +out: +dso__set_loaded (dso, map->type); +pthread_mutex_unlock (&dso->lock); +return ret; }
Signature · 1 line
2.4.0
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf symbols: Protect dso symbol loading using a mutex
perf symbols: Protect dso symbol loading using a mutex
Commit-ID: 4a936edc317005e8cd2b501e7865721bec104544 Gitweb: http://git.kernel.org/tip/4a936edc317005e8cd2b501e7865721bec104544 Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 18 May 2015 09:30:40 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Mon, 18 May 2015 10:17:36 -0300
perf symbols: Protect dso symbol loading using a mutex
Add mutex to protect it from concurrent dso__load().
--- tools/perf/util/dso.c | 2 ++ tools/perf/util/dso.h | 1 + tools/perf/util/symbol.c | 34 ++++++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 10 deletions(-)
tools/perf/util/dso.c +2 -0
@@ -936,6 +936,7 @@ struct dso *dso__new(const char *name)RB_CLEAR_NODE (&dso->rb_node);INIT_LIST_HEAD (&dso->node);INIT_LIST_HEAD (&dso->data.open_entry); +pthread_mutex_init (&dso->lock,NULL ); }return dso;
@@ -966,6 +967,7 @@ void dso__delete(struct dso *dso)dso_cache__free (&dso->data.cache);dso__free_a2l (dso);zfree (&dso->symsrc_filename); +pthread_mutex_destroy (&dso->lock);free (dso); }
tools/perf/util/dso.h +1 -0
@@ -129,6 +129,7 @@ struct dsos {struct auxtrace_cache ;struct dso { +pthread_mutex_t lock;struct list_head node;struct rb_node rb_node;/* rbtree node sorted by long name */ struct rb_root symbols[MAP__NR_TYPES ];
tools/perf/util/symbol.c +24 -10
@@ -1383,12 +1383,22 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)struct symsrc *syms_ss =NULL , *runtime_ss =NULL ;bool kmod; -dso__set_loaded (dso, map->type); +pthread_mutex_lock (&dso->lock); + +/* check again under the dso->lock */ +if (dso__loaded (dso, map->type)) { + ret =1 ; + goto out; + } + +if (dso->kernel) { +if (dso->kernel ==DSO_TYPE_KERNEL ) + ret =dso__load_kernel_sym (dso, map, filter); +else if (dso->kernel ==DSO_TYPE_GUEST_KERNEL ) + ret =dso__load_guest_kernel_sym (dso, map, filter); -if (dso->kernel ==DSO_TYPE_KERNEL ) -return dso__load_kernel_sym (dso, map, filter); -else if (dso->kernel ==DSO_TYPE_GUEST_KERNEL ) -return dso__load_guest_kernel_sym (dso, map, filter); + goto out; + }if (map->groups && map->groups->machine) machine = map->groups->machine;
@@ -1401,18 +1411,18 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)struct stat st;if (lstat (dso->name, &st) <0 ) -return -1 ; + goto out;if (st.st_uid && (st.st_uid !=geteuid ())) {pr_warning ("File %s not owned by current user or root, " "ignoring it.\n" , dso->name); -return -1 ; + goto out; } ret =dso__load_perf_map (dso, map, filter); dso->symtab_type = ret >0 ?DSO_BINARY_TYPE__JAVA_JIT :DSO_BINARY_TYPE__NOT_FOUND ; -return ret; + goto out; }if (machine)
@@ -1420,7 +1430,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) name =malloc (PATH_MAX );if (!name) -return -1 ; + goto out; kmod = dso->symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
@@ -1501,7 +1511,11 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) out_free:free (name);if (ret <0 &&strstr (dso->name," (deleted)" ) !=NULL ) -return 0 ; + ret =0 ; +out: +dso__set_loaded (dso, map->type); +pthread_mutex_unlock (&dso->lock); +return ret; }
Namhyung Kim
maintainerPATCH 26/40 perf tools: Protect dso cache tree using dso->lock
In reply to Namhyung KimThe dso cache is accessed during dwarf callchain unwind and it might be processed concurrently when multi-thread report is enabled. Protect it under dso->lock.
Note that it doesn't protect dso_cache__find(). I think it's safe to access to the cache tree without the lock since we don't delete nodes. It it missed an existing node due to rotation, it'll find it during dso_cache__insert() anyway.
--- tools/perf/util/dso.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-)
tools/perf/util/dso.c +27 -7
@@ -495,10 +495,12 @@ bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by) }static void -dso_cache__free (struct rb_root *root) +dso_cache__free (struct dso *dso) { +struct rb_root *root = &dso->data.cache;struct rb_node *next =rb_first (root); +pthread_mutex_lock (&dso->lock);while (next) {struct dso_cache *cache;
@@ -507,10 +509,12 @@ dso_cache__free(struct rb_root *root)rb_erase (&cache->rb_node, root);free (cache); } +pthread_mutex_unlock (&dso->lock); } -static struct dso_cache *dso_cache__find (const struct rb_root *root,u64 offset) +static struct dso_cache *dso_cache__find (struct dso *dso,u64 offset) { +const struct rb_root *root = &dso->data.cache;struct rb_node *const *p = &root->rb_node;const struct rb_node *parent =NULL ;struct dso_cache *cache;
@@ -529,17 +533,20 @@ static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)else return cache; } +return NULL ; } -static void -dso_cache__insert (struct rb_root *root,struct dso_cache *new) +static struct dso_cache * +dso_cache__insert (struct dso *dso,struct dso_cache *new) { +struct rb_root *root = &dso->data.cache;struct rb_node **p = &root->rb_node;struct rb_node *parent =NULL ;struct dso_cache *cache;u64 offset = new->offset; +pthread_mutex_lock (&dso->lock);while (*p !=NULL ) {u64 end;
@@ -551,10 +558,17 @@ dso_cache__insert(struct rb_root *root, struct dso_cache *new) p = &(*p)->rb_left; else if (offset >= end) p = &(*p)->rb_right; + else + goto out; }rb_link_node (&new->rb_node, parent, p);rb_insert_color (&new->rb_node, root); + + cache =NULL ; +out: +pthread_mutex_unlock (&dso->lock); +return cache; } static ssize_t
@@ -572,6 +586,7 @@ static ssize_t dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) { struct dso_cache *cache; +struct dso_cache *old;ssize_t ret; do {
@@ -591,7 +606,12 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) cache->offset = cache_offset; cache->size = ret; -dso_cache__insert (&dso->data.cache, cache); + old =dso_cache__insert (dso, cache); +if (old) { +/* we lose the race */ +free (cache); + cache = old; + } ret =dso_cache__memcpy (cache, offset, data, size);
@@ -608,7 +628,7 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset, {struct dso_cache *cache; - cache =dso_cache__find (&dso->data.cache, offset); + cache =dso_cache__find (dso, offset);if (cache)return dso_cache__memcpy (cache, offset, data, size);else
@@ -964,7 +984,7 @@ void dso__delete(struct dso *dso)dso__data_close (dso);auxtrace_cache__free (dso->auxtrace_cache); -dso_cache__free (&dso->data.cache); +dso_cache__free (dso);dso__free_a2l (dso);zfree (&dso->symsrc_filename);pthread_mutex_destroy (&dso->lock);
Signature · 1 line
2.4.0
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf symbols: Protect dso cache tree using dso-> lock
perf symbols: Protect dso cache tree using dso->lock
Commit-ID: 8e67b7258e582998ab635bdc3c884d7a8077af5b Gitweb: http://git.kernel.org/tip/8e67b7258e582998ab635bdc3c884d7a8077af5b Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 18 May 2015 09:30:41 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Mon, 18 May 2015 10:17:37 -0300
perf symbols: Protect dso cache tree using dso->lock
The dso cache is accessed during dwarf callchain unwind and it might be processed concurrently. Protect it under dso->lock.
Note that it doesn't protect dso_cache__find(). I think it's safe to access to the cache tree without the lock since we don't delete nodes.
It it missed an existing node due to rotation, it'll find it during dso_cache__insert() anyway.
--- tools/perf/util/dso.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-)
tools/perf/util/dso.c +27 -7
@@ -495,10 +495,12 @@ bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by) }static void -dso_cache__free (struct rb_root *root) +dso_cache__free (struct dso *dso) { +struct rb_root *root = &dso->data.cache;struct rb_node *next =rb_first (root); +pthread_mutex_lock (&dso->lock);while (next) {struct dso_cache *cache;
@@ -507,10 +509,12 @@ dso_cache__free(struct rb_root *root)rb_erase (&cache->rb_node, root);free (cache); } +pthread_mutex_unlock (&dso->lock); } -static struct dso_cache *dso_cache__find (const struct rb_root *root,u64 offset) +static struct dso_cache *dso_cache__find (struct dso *dso,u64 offset) { +const struct rb_root *root = &dso->data.cache;struct rb_node *const *p = &root->rb_node;const struct rb_node *parent =NULL ;struct dso_cache *cache;
@@ -529,17 +533,20 @@ static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)else return cache; } +return NULL ; } -static void -dso_cache__insert (struct rb_root *root,struct dso_cache *new) +static struct dso_cache * +dso_cache__insert (struct dso *dso,struct dso_cache *new) { +struct rb_root *root = &dso->data.cache;struct rb_node **p = &root->rb_node;struct rb_node *parent =NULL ;struct dso_cache *cache;u64 offset = new->offset; +pthread_mutex_lock (&dso->lock);while (*p !=NULL ) {u64 end;
@@ -551,10 +558,17 @@ dso_cache__insert(struct rb_root *root, struct dso_cache *new) p = &(*p)->rb_left; else if (offset >= end) p = &(*p)->rb_right; + else + goto out; }rb_link_node (&new->rb_node, parent, p);rb_insert_color (&new->rb_node, root); + + cache =NULL ; +out: +pthread_mutex_unlock (&dso->lock); +return cache; } static ssize_t
@@ -572,6 +586,7 @@ static ssize_t dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) { struct dso_cache *cache; +struct dso_cache *old;ssize_t ret; do {
@@ -591,7 +606,12 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) cache->offset = cache_offset; cache->size = ret; -dso_cache__insert (&dso->data.cache, cache); + old =dso_cache__insert (dso, cache); +if (old) { +/* we lose the race */ +free (cache); + cache = old; + } ret =dso_cache__memcpy (cache, offset, data, size);
@@ -608,7 +628,7 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset, {struct dso_cache *cache; - cache =dso_cache__find (&dso->data.cache, offset); + cache =dso_cache__find (dso, offset);if (cache)return dso_cache__memcpy (cache, offset, data, size);else
@@ -964,7 +984,7 @@ void dso__delete(struct dso *dso)dso__data_close (dso);auxtrace_cache__free (dso->auxtrace_cache); -dso_cache__free (&dso->data.cache); +dso_cache__free (dso);dso__free_a2l (dso);zfree (&dso->symsrc_filename);pthread_mutex_destroy (&dso->lock);
Namhyung Kim
maintainerPATCH 27/40 perf tools: Protect dso cache fd with a mutex
In reply to Namhyung KimWhen dso cache is accessed in multi-thread environment, it's possible to close other dso->data.fd during operation due to open file limit. Protect the file descriptors using a separate mutex.
--- tools/perf/util/dso.c | 98 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 26 deletions(-)
tools/perf/util/dso.c +72 -26
@@ -265,6 +265,7 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,*/ static LIST_HEAD (dso__data_open);static long dso__data_open_cnt; +static pthread_mutex_t dso__data_open_lock =PTHREAD_MUTEX_INITIALIZER ;static void dso__list_add (struct dso *dso) {
@@ -434,7 +435,9 @@ static void check_data_close(void)*/ void dso__data_close (struct dso *dso) { +pthread_mutex_lock (&dso__data_open_lock);close_dso (dso); +pthread_mutex_unlock (&dso__data_open_lock); }/**
@@ -457,6 +460,8 @@ int dso__data_fd(struct dso *dso, struct machine *machine)if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; +pthread_mutex_lock (&dso__data_open_lock); +if (dso->data.fd >=0 ) goto out;
@@ -479,6 +484,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine)else dso->data.status =DSO_DATA_STATUS_ERROR ; +pthread_mutex_unlock (&dso__data_open_lock);return dso->data.fd; }
@@ -583,7 +589,8 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset, }static ssize_t -dso_cache__read (struct dso *dso,u64 offset,u8 *data,ssize_t size) +dso_cache__read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {struct dso_cache *cache;struct dso_cache *old;
@@ -592,11 +599,24 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) do { u64 cache_offset; - ret = -ENOMEM; - cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); if (!cache) - break; + return -ENOMEM; + + pthread_mutex_lock(&dso__data_open_lock); + + /* + * dso->data.fd might be closed if other thread opened another + * file (dso) due to open file limit (RLIMIT_NOFILE). + */ + if (dso->data.fd < 0) { + dso->data.fd = open_dso(dso, machine); + if (dso->data.fd < 0) { + ret = -errno; + dso->data.status = DSO_DATA_STATUS_ERROR; + break; + } + } cache_offset = offset & DSO__DATA_CACHE_MASK;
@@ -606,6 +626,11 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) cache->offset = cache_offset; cache->size = ret; + }while (0 ); + +pthread_mutex_unlock (&dso__data_open_lock); + +if (ret >0 ) { old =dso_cache__insert (dso, cache);if (old) {/* we lose the race */
@@ -614,8 +639,7 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) } ret =dso_cache__memcpy (cache, offset, data, size); - - }while (0 ); + }if (ret <=0 )free (cache);
@@ -623,8 +647,8 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)return ret; } -static ssize_t dso_cache_read (struct dso *dso,u64 offset, -u8 *data,ssize_t size) +static ssize_t dso_cache_read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {struct dso_cache *cache;
@@ -632,7 +656,7 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,if (cache)return dso_cache__memcpy (cache, offset, data, size);else -return dso_cache__read (dso, offset, data, size); +return dso_cache__read (dso, machine, offset, data, size); }/*
@@ -640,7 +664,8 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,* in the rb_tree. Any read to already cached data is served * by cached data. */ -static ssize_t cached_read (struct dso *dso,u64 offset,u8 *data,ssize_t size) +static ssize_t cached_read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {ssize_t r =0 ;u8 *p = data;
@@ -648,7 +673,7 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) do { ssize_t ret; - ret = dso_cache_read(dso, offset, p, size); + ret = dso_cache_read(dso, machine, offset, p, size); if (ret < 0) return ret;
@@ -668,21 +693,42 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)return r; } -static int data_file_size (struct dso *dso) +static int data_file_size (struct dso *dso,struct machine *machine) { +int ret =0 ;struct stat st;char sbuf[STRERR_BUFSIZE ]; -if (!dso->data.file_size) { -if (fstat (dso->data.fd, &st)) { -pr_err ("dso mmap failed, fstat: %s\n" , -strerror_r (errno, sbuf,sizeof (sbuf))); -return -1 ; +if (dso->data.file_size) +return 0 ; + +pthread_mutex_lock (&dso__data_open_lock); + +/* +* dso->data.fd might be closed if other thread opened another +* file (dso) due to open file limit (RLIMIT_NOFILE). +*/ +if (dso->data.fd <0 ) { + dso->data.fd =open_dso (dso, machine); +if (dso->data.fd <0 ) { + ret = -errno; + dso->data.status =DSO_DATA_STATUS_ERROR ; + goto out; } - dso->data.file_size = st.st_size; } -return 0 ; +if (fstat (dso->data.fd, &st) <0 ) { + ret = -errno; +pr_err ("dso cache fstat failed: %s\n" , +strerror_r (errno, sbuf,sizeof (sbuf))); + dso->data.status =DSO_DATA_STATUS_ERROR ; + goto out; + } + dso->data.file_size = st.st_size; + +out: +pthread_mutex_unlock (&dso__data_open_lock); +return ret; }/**
@@ -700,17 +746,17 @@ off_t dso__data_size(struct dso *dso, struct machine *machine)if (fd <0 )return fd; -if (data_file_size (dso)) +if (data_file_size (dso, machine))return -1 ;/* For now just estimate dso data size is close to file size */ return dso->data.file_size; } -static ssize_t data_read_offset (struct dso *dso,u64 offset, -u8 *data,ssize_t size) +static ssize_t data_read_offset (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) { -if (data_file_size (dso)) +if (data_file_size (dso, machine))return -1 ;/* Check the offset sanity. */
@@ -720,7 +766,7 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,if (offset + size < offset)return -1 ; -return cached_read (dso, offset, data, size); +return cached_read (dso, machine, offset, data, size); }/**
@@ -737,10 +783,10 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,ssize_t dso__data_read_offset (struct dso *dso,struct machine *machine,u64 offset,u8 *data,ssize_t size) { -if (dso__data_fd (dso, machine) <0 ) +if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; -return data_read_offset (dso, offset, data, size); +return data_read_offset (dso, machine, offset, data, size); }/**
Signature · 1 line
2.4.0
tip-bot for Namhyung Kim
merge bot[tip:perf/core] perf tools: Protect dso cache fd with a mutex
perf tools: Protect dso cache fd with a mutex
Commit-ID: 33bdedcea2d77231fe46b1204cf6fc3a7d7c96c9 Gitweb: http://git.kernel.org/tip/33bdedcea2d77231fe46b1204cf6fc3a7d7c96c9 Author: Namhyung Kim <[email protected]> AuthorDate: Mon, 18 May 2015 09:30:42 +0900 Committer: Arnaldo Carvalho de Melo <[email protected]> CommitDate: Mon, 18 May 2015 10:17:38 -0300
perf tools: Protect dso cache fd with a mutex
When dso cache is accessed in multi-thread environment, it's possible to close other dso->data.fd during operation due to open file limit. Protect the file descriptors using a separate mutex.
--- tools/perf/util/dso.c | 98 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 26 deletions(-)
tools/perf/util/dso.c +72 -26
@@ -265,6 +265,7 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,*/ static LIST_HEAD (dso__data_open);static long dso__data_open_cnt; +static pthread_mutex_t dso__data_open_lock =PTHREAD_MUTEX_INITIALIZER ;static void dso__list_add (struct dso *dso) {
@@ -434,7 +435,9 @@ static void check_data_close(void)*/ void dso__data_close (struct dso *dso) { +pthread_mutex_lock (&dso__data_open_lock);close_dso (dso); +pthread_mutex_unlock (&dso__data_open_lock); }/**
@@ -457,6 +460,8 @@ int dso__data_fd(struct dso *dso, struct machine *machine)if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; +pthread_mutex_lock (&dso__data_open_lock); +if (dso->data.fd >=0 ) goto out;
@@ -479,6 +484,7 @@ out:else dso->data.status =DSO_DATA_STATUS_ERROR ; +pthread_mutex_unlock (&dso__data_open_lock);return dso->data.fd; }
@@ -583,7 +589,8 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset, }static ssize_t -dso_cache__read (struct dso *dso,u64 offset,u8 *data,ssize_t size) +dso_cache__read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {struct dso_cache *cache;struct dso_cache *old;
@@ -592,11 +599,24 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) do { u64 cache_offset; - ret = -ENOMEM; - cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); if (!cache) - break; + return -ENOMEM; + + pthread_mutex_lock(&dso__data_open_lock); + + /* + * dso->data.fd might be closed if other thread opened another + * file (dso) due to open file limit (RLIMIT_NOFILE). + */ + if (dso->data.fd < 0) { + dso->data.fd = open_dso(dso, machine); + if (dso->data.fd < 0) { + ret = -errno; + dso->data.status = DSO_DATA_STATUS_ERROR; + break; + } + } cache_offset = offset & DSO__DATA_CACHE_MASK;
@@ -606,6 +626,11 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) cache->offset = cache_offset; cache->size = ret; + }while (0 ); + +pthread_mutex_unlock (&dso__data_open_lock); + +if (ret >0 ) { old =dso_cache__insert (dso, cache);if (old) {/* we lose the race */
@@ -614,8 +639,7 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) } ret =dso_cache__memcpy (cache, offset, data, size); - - }while (0 ); + }if (ret <=0 )free (cache);
@@ -623,8 +647,8 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)return ret; } -static ssize_t dso_cache_read (struct dso *dso,u64 offset, -u8 *data,ssize_t size) +static ssize_t dso_cache_read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {struct dso_cache *cache;
@@ -632,7 +656,7 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,if (cache)return dso_cache__memcpy (cache, offset, data, size);else -return dso_cache__read (dso, offset, data, size); +return dso_cache__read (dso, machine, offset, data, size); }/*
@@ -640,7 +664,8 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,* in the rb_tree. Any read to already cached data is served * by cached data. */ -static ssize_t cached_read (struct dso *dso,u64 offset,u8 *data,ssize_t size) +static ssize_t cached_read (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) {ssize_t r =0 ;u8 *p = data;
@@ -648,7 +673,7 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) do { ssize_t ret; - ret = dso_cache_read(dso, offset, p, size); + ret = dso_cache_read(dso, machine, offset, p, size); if (ret < 0) return ret;
@@ -668,21 +693,42 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)return r; } -static int data_file_size (struct dso *dso) +static int data_file_size (struct dso *dso,struct machine *machine) { +int ret =0 ;struct stat st;char sbuf[STRERR_BUFSIZE ]; -if (!dso->data.file_size) { -if (fstat (dso->data.fd, &st)) { -pr_err ("dso mmap failed, fstat: %s\n" , -strerror_r (errno, sbuf,sizeof (sbuf))); -return -1 ; +if (dso->data.file_size) +return 0 ; + +pthread_mutex_lock (&dso__data_open_lock); + +/* +* dso->data.fd might be closed if other thread opened another +* file (dso) due to open file limit (RLIMIT_NOFILE). +*/ +if (dso->data.fd <0 ) { + dso->data.fd =open_dso (dso, machine); +if (dso->data.fd <0 ) { + ret = -errno; + dso->data.status =DSO_DATA_STATUS_ERROR ; + goto out; } - dso->data.file_size = st.st_size; } -return 0 ; +if (fstat (dso->data.fd, &st) <0 ) { + ret = -errno; +pr_err ("dso cache fstat failed: %s\n" , +strerror_r (errno, sbuf,sizeof (sbuf))); + dso->data.status =DSO_DATA_STATUS_ERROR ; + goto out; + } + dso->data.file_size = st.st_size; + +out: +pthread_mutex_unlock (&dso__data_open_lock); +return ret; }/**
@@ -700,17 +746,17 @@ off_t dso__data_size(struct dso *dso, struct machine *machine)if (fd <0 )return fd; -if (data_file_size (dso)) +if (data_file_size (dso, machine))return -1 ;/* For now just estimate dso data size is close to file size */ return dso->data.file_size; } -static ssize_t data_read_offset (struct dso *dso,u64 offset, -u8 *data,ssize_t size) +static ssize_t data_read_offset (struct dso *dso,struct machine *machine, +u64 offset,u8 *data,ssize_t size) { -if (data_file_size (dso)) +if (data_file_size (dso, machine))return -1 ;/* Check the offset sanity. */
@@ -720,7 +766,7 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,if (offset + size < offset)return -1 ; -return cached_read (dso, offset, data, size); +return cached_read (dso, machine, offset, data, size); }/**
@@ -737,10 +783,10 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,ssize_t dso__data_read_offset (struct dso *dso,struct machine *machine,u64 offset,u8 *data,ssize_t size) { -if (dso__data_fd (dso, machine) <0 ) +if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; -return data_read_offset (dso, offset, data, size); +return data_read_offset (dso, machine, offset, data, size); }/**
Namhyung Kim
maintainerPATCH 28/40 perf callchain: Maintain libunwind's address space in map_groups
In reply to Namhyung KimCurrently the address_space was kept in thread struct but it's more appropriate to keep it in map_groups as it's maintained with time. Also we don't need to flush after exec since it still can be accessed when used with an indexed data file.
--- tools/perf/tests/dwarf-unwind.c | 4 ++-- tools/perf/util/map.c | 5 +++++ tools/perf/util/map.h | 1 + tools/perf/util/thread.c | 7 ------- tools/perf/util/unwind-libunwind.c | 28 +++++++++++++--------------- tools/perf/util/unwind.h | 15 ++++++--------- 6 files changed, 27 insertions(+), 33 deletions(-)
tools/perf/tests/dwarf-unwind.c +2 -2
@@ -143,6 +143,8 @@ int test__dwarf_unwind(void)struct thread *thread;int err =-1 ; + callchain_param.record_mode =CALLCHAIN_DWARF ; +machines__init (&machines); machine =machines__find (&machines,HOST_KERNEL_ID );
@@ -151,8 +153,6 @@ int test__dwarf_unwind(void)return -1 ; } - callchain_param.record_mode =CALLCHAIN_DWARF ; -if (init_live_machine (machine)) {pr_err ("Could not init machine\n" ); goto out;
tools/perf/util/map.c +5 -0
@@ -14,6 +14,7 @@#include "util.h" #include "debug.h" #include "machine.h" +#include "unwind.h" #include <linux/string.h> const char *map_type__name[MAP__NR_TYPES ] = {
@@ -431,6 +432,8 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) mg->refcnt =1 ; mg->timestamp =0 ;INIT_LIST_HEAD (&mg->list); + +unwind__prepare_access (mg); }static void maps__delete (struct rb_root *maps)
@@ -464,6 +467,8 @@ void map_groups__exit(struct map_groups *mg)maps__delete (&mg->maps[i]);maps__delete_removed (&mg->removed_maps[i]); } + +unwind__finish_access (mg); }bool map_groups__empty (struct map_groups *mg)
tools/perf/util/map.h +1 -0
@@ -67,6 +67,7 @@ struct map_groups {u64 timestamp;int refcnt;struct list_head list; +void *priv; };struct map_groups *map_groups__new (struct machine *machine);
tools/perf/util/thread.c +0 -7
@@ -105,9 +105,6 @@ struct thread *thread__new(pid_t pid, pid_t tid)INIT_LIST_HEAD (&thread->tid_node);INIT_LIST_HEAD (&thread->mg_list); -if (unwind__prepare_access (thread) <0 ) - goto err_thread; - comm_str =malloc (32 );if (!comm_str) goto err_thread;
@@ -153,7 +150,6 @@ void thread__delete(struct thread *thread)list_del (&comm->list);comm__free (comm); } -unwind__finish_access (thread);free (thread); }
@@ -250,9 +246,6 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,break ; }list_add_tail (&new->list, &curr->list); - -if (exec) -unwind__flush_access (thread); }if (exec) {
tools/perf/util/unwind-libunwind.c +13 -15
@@ -32,6 +32,7 @@#include "symbol.h" #include "util.h" #include "debug.h" +#include "map.h" extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
@@ -561,7 +562,7 @@ static unw_accessors_t accessors = { .get_proc_name = get_proc_name, }; -int unwind__prepare_access (struct thread *thread) +int unwind__prepare_access (struct map_groups *mg) {unw_addr_space_t addr_space;
@@ -575,41 +576,38 @@ int unwind__prepare_access(struct thread *thread) }unw_set_caching_policy (addr_space,UNW_CACHE_GLOBAL ); -thread__set_priv (thread, addr_space); + mg->priv = addr_space;return 0 ; } -void unwind__flush_access (struct thread *thread) +void unwind__finish_access (struct map_groups *mg) { -unw_addr_space_t addr_space; +unw_addr_space_t addr_space = mg->priv;if (callchain_param.record_mode !=CALLCHAIN_DWARF )return ; - addr_space =thread__priv (thread); -unw_flush_cache (addr_space,0 ,0 ); -} - -void unwind__finish_access (struct thread *thread) -{ -unw_addr_space_t addr_space; - -if (callchain_param.record_mode !=CALLCHAIN_DWARF ) +if (addr_space ==NULL )return ; - addr_space =thread__priv (thread);unw_destroy_addr_space (addr_space); + mg->priv =NULL ; }static int get_entries (struct unwind_info *ui,unwind_entry_cb_t cb,void *arg,int max_stack) { +struct map_groups *mg;unw_addr_space_t addr_space;unw_cursor_t c;int ret; - addr_space =thread__priv (ui->thread); + mg =thread__get_map_groups (ui->thread, ui->sample->time); +if (mg ==NULL ) +return -1 ; + + addr_space = mg->priv;if (addr_space ==NULL )return -1 ;
tools/perf/util/unwind.h +6 -9
@@ -21,17 +21,15 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,/* libunwind specific */ #ifdef HAVE_LIBUNWIND_SUPPORT int libunwind__arch_reg_id (int regnum); -int unwind__prepare_access (struct thread *thread); -void unwind__flush_access (struct thread *thread); -void unwind__finish_access (struct thread *thread); +int unwind__prepare_access (struct map_groups *mg); +void unwind__finish_access (struct map_groups *mg);#else -static inline int unwind__prepare_access (struct thread *thread__maybe_unused ) +static inline int unwind__prepare_access (struct map_groups *mg__maybe_unused ) {return 0 ; } -static inline void unwind__flush_access (struct thread *thread__maybe_unused ) {} -static inline void unwind__finish_access (struct thread *thread__maybe_unused ) {} +static inline void unwind__finish_access (struct map_groups *mg__maybe_unused ) {}#endif #else static inline int
@@ -44,12 +42,11 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,return 0 ; } -static inline int unwind__prepare_access (struct thread *thread__maybe_unused ) +static inline int unwind__prepare_access (struct map_groups *mg__maybe_unused ) {return 0 ; } -static inline void unwind__flush_access (struct thread *thread__maybe_unused ) {} -static inline void unwind__finish_access (struct thread *thread__maybe_unused ) {} +static inline void unwind__finish_access (struct map_groups *mg__maybe_unused ) {}#endif /* HAVE_DWARF_UNWIND_SUPPORT */ #endif /* __UNWIND_H */
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 29/40 perf tools: Add dso__data_get/put_fd()
In reply to Namhyung KimUsing dso__data_fd() in multi-thread environment is not safe since returned fd can be closed and/or reused anytime. So convert it to the dso__data_get/put_fd() pair to protect the access with lock.
The original dso__data_fd() is deprecated and kept only for testing.
--- tools/perf/util/dso.c | 44 +++++++++++++++++++++++++++++--------- tools/perf/util/dso.h | 9 ++++++-- tools/perf/util/unwind-libunwind.c | 38 +++++++++++++++++++------------- 3 files changed, 64 insertions(+), 27 deletions(-)
tools/perf/util/dso.c +34 -10
@@ -441,14 +441,15 @@ void dso__data_close(struct dso *dso) }/** -* dso__data_fd - Get dso's data file descriptor +* dso__data_get_fd - Get dso's data file descriptor * @dso: dso object * @machine: machine object * * External interface to find dso's file, open it and -* returns file descriptor. +* returns file descriptor. Should be paired with +* dso__data_put_fd(). */ -int dso__data_fd (struct dso *dso,struct machine *machine) +int dso__data_get_fd (struct dso *dso,struct machine *machine) {enum dso_binary_type binary_type_data[] = {DSO_BINARY_TYPE__BUILD_ID_CACHE ,
@@ -457,11 +458,11 @@ int dso__data_fd(struct dso *dso, struct machine *machine) };int i =0 ; +pthread_mutex_lock (&dso__data_open_lock); +if (dso->data.status ==DSO_DATA_STATUS_ERROR )return -1 ; -pthread_mutex_lock (&dso__data_open_lock); -if (dso->data.fd >=0 ) goto out;
@@ -484,10 +485,31 @@ int dso__data_fd(struct dso *dso, struct machine *machine)else dso->data.status =DSO_DATA_STATUS_ERROR ; -pthread_mutex_unlock (&dso__data_open_lock);return dso->data.fd; } +void dso__data_put_fd (struct dso *dso__maybe_unused ) +{ +pthread_mutex_unlock (&dso__data_open_lock); +} + +/** +* dso__data_get_fd - Get dso's data file descriptor +* @dso: dso object +* @machine: machine object +* +* Obsolete interface to find dso's file, open it and +* returns file descriptor. It's not thread-safe in that +* the returned fd may be reused for other file. +*/ +int dso__data_fd (struct dso *dso,struct machine *machine) +{ +int fd =dso__data_get_fd (dso, machine); + +dso__data_put_fd (dso); +return fd; +} +bool dso__data_status_seen (struct dso *dso,enum dso_data_status_seen by) {u32 flag =1 << by;
@@ -1200,12 +1222,14 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)enum dso_type dso__type (struct dso *dso,struct machine *machine) {int fd; +enum dso_type type =DSO__TYPE_UNKNOWN ; - fd =dso__data_fd (dso, machine); -if (fd <0 ) -return DSO__TYPE_UNKNOWN ; + fd =dso__data_get_fd (dso, machine); +if (fd >=0 ) + type =dso__type_fd (fd); +dso__data_put_fd (dso); -return dso__type_fd (fd); +return type; }int dso__strerror_load (struct dso *dso,char *buf,size_t buflen)
tools/perf/util/dso.h +7 -2
@@ -240,7 +240,9 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,/* * The dso__data_* external interface provides following functions: -* dso__data_fd +* dso__data_fd (obsolete) +* dso__data_get_fd +* dso__data_put_fd * dso__data_close * dso__data_size * dso__data_read_offset
@@ -257,8 +259,9 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,* The current usage of the dso__data_* interface is as follows: * * Get DSO's fd: -* int fd = dso__data_fd(dso, machine); +* int fd = dso__data_get_fd(dso, machine); * USE 'fd' SOMEHOW +* dso__data_put_fd(dso) * * Read DSO's data: * n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE);
@@ -278,6 +281,8 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,* TODO */ int dso__data_fd (struct dso *dso,struct machine *machine); +int dso__data_get_fd (struct dso *dso,struct machine *machine); +void dso__data_put_fd (struct dso *dso);void dso__data_close (struct dso *dso);off_t dso__data_size (struct dso *dso,struct machine *machine);
tools/perf/util/unwind-libunwind.c +23 -15
@@ -270,13 +270,13 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,u64 offset = dso->data.eh_frame_hdr_offset;if (offset ==0 ) { - fd =dso__data_fd (dso, machine); -if (fd <0 ) -return -EINVAL ; - -/* Check the .eh_frame section for unwinding info */ - offset =elf_section_offset (fd,".eh_frame_hdr" ); - dso->data.eh_frame_hdr_offset = offset; + fd =dso__data_get_fd (dso, machine); +if (fd >=0 ) { +/* Check the .eh_frame section for unwinding info */ + offset =elf_section_offset (fd,".eh_frame_hdr" ); + dso->data.eh_frame_hdr_offset = offset; + } +dso__data_put_fd (dso); }if (offset)
@@ -295,13 +295,19 @@ static int read_unwind_spec_debug_frame(struct dso *dso,u64 ofs = dso->data.debug_frame_offset;if (ofs ==0 ) { - fd =dso__data_fd (dso, machine); -if (fd <0 ) -return -EINVAL ; - -/* Check the .debug_frame section for unwinding info */ - ofs =elf_section_offset (fd,".debug_frame" ); - dso->data.debug_frame_offset = ofs; +int ret =0 ; + + fd =dso__data_get_fd (dso, machine); +if (fd >=0 ) { +/* Check the .debug_frame section for unwinding info */ + ofs =elf_section_offset (fd,".debug_frame" ); + dso->data.debug_frame_offset = ofs; + }else + ret = -EINVAL ; + +dso__data_put_fd (dso); +if (ret) +return ret; } *offset = ofs;
@@ -356,10 +362,12 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,#ifndef NO_LIBUNWIND_DEBUG_FRAME /* Check the .debug_frame section for unwinding info */ if (!read_unwind_spec_debug_frame (map->dso, ui->machine, &segbase)) { -int fd =dso__data_fd (map->dso, ui->machine); +int fd =dso__data_get_fd (map->dso, ui->machine);int is_exec =elf_is_exec (fd, map->dso->name);unw_word_t base = is_exec ?0 : map->start; +dso__data_put_fd (map->dso); +memset (&di,0 ,sizeof (di));if (dwarf_find_debug_frame (0 , &di, ip, base, map->dso->name, map->start, map->end))
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 30/40 perf session: Pass struct events stats to event processing functions
In reply to Namhyung KimPass stats structure so that it can point separate object when used in multi-thread environment.
--- tools/perf/util/session.c | 71 ++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 26 deletions(-)
tools/perf/util/session.c +45 -26
@@ -18,6 +18,7 @@#include "auxtrace.h" static int perf_session__deliver_event(struct perf_session *session, + struct events_stats *stats, union perf_event *event, struct perf_sample *sample, struct perf_tool *tool,
@@ -106,7 +107,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe,return ret; } -return perf_session__deliver_event (session, event->event, &sample, +return perf_session__deliver_event (session, &session->evlist->stats, + event->event, &sample, session->tool, event->file_offset); }
@@ -942,6 +944,7 @@ static struct machine *machines__find_for_cpumode(struct machines *machines, } static int deliver_sample_value(struct perf_evlist *evlist, + struct events_stats *stats, struct perf_tool *tool, union perf_event *event, struct perf_sample *sample,
@@ -957,7 +960,7 @@ static int deliver_sample_value(struct perf_evlist *evlist, }if (!sid || sid->evsel ==NULL ) { - ++evlist->stats.nr_unknown_id; + ++stats->nr_unknown_id;return 0 ; }
@@ -965,6 +968,7 @@ static int deliver_sample_value(struct perf_evlist *evlist, } static int deliver_sample_group(struct perf_evlist *evlist, + struct events_stats *stats, struct perf_tool *tool, union perf_event *event, struct perf_sample *sample,
@@ -974,7 +978,7 @@ static int deliver_sample_group(struct perf_evlist *evlist,u64 i;for (i =0 ; i < sample->read.group.nr; i++) { - ret =deliver_sample_value (evlist, tool, event, sample, + ret =deliver_sample_value (evlist, stats, tool, event, sample, &sample->read.group.values[i], machine);if (ret)
@@ -986,6 +990,7 @@ static int deliver_sample_group(struct perf_evlist *evlist, static int perf_evlist__deliver_sample(struct perf_evlist *evlist, + struct events_stats *stats, struct perf_tool *tool, union perf_event *event, struct perf_sample *sample,
@@ -1002,14 +1007,15 @@ static int/* For PERF_SAMPLE_READ we have either single or group mode. */ if (read_format &PERF_FORMAT_GROUP ) -return deliver_sample_group (evlist, tool, event, sample, +return deliver_sample_group (evlist, stats, tool, event, sample, machine);else -return deliver_sample_value (evlist, tool, event, sample, +return deliver_sample_value (evlist, stats, tool, event, sample, &sample->read.one, machine); } static int machines__deliver_event(struct machines *machines, + struct events_stats *stats, struct perf_evlist *evlist, union perf_event *event, struct perf_sample *sample,
@@ -1028,14 +1034,15 @@ static int machines__deliver_event(struct machines *machines,case PERF_RECORD_SAMPLE :dump_sample (evsel, event, sample);if (evsel ==NULL ) { - ++evlist->stats.nr_unknown_id; + ++stats->nr_unknown_id;return 0 ; }if (machine ==NULL ) { - ++evlist->stats.nr_unprocessable_samples; + ++stats->nr_unprocessable_samples;return 0 ; } -return perf_evlist__deliver_sample (evlist, tool, event, sample, evsel, machine); +return perf_evlist__deliver_sample (evlist, stats, tool, event, + sample, evsel, machine);case PERF_RECORD_MMAP :return tool->mmap (tool, event, sample, machine);case PERF_RECORD_MMAP2 :
@@ -1048,7 +1055,7 @@ static int machines__deliver_event(struct machines *machines,return tool->exit (tool, event, sample, machine);case PERF_RECORD_LOST :if (tool->lost == perf_event__process_lost) - evlist->stats.total_lost += event->lost.lost; + stats->total_lost += event->lost.lost;return tool->lost (tool, event, sample, machine);case PERF_RECORD_READ :return tool->read (tool, event, sample, evsel, machine);
@@ -1061,12 +1068,13 @@ static int machines__deliver_event(struct machines *machines,case PERF_RECORD_ITRACE_START :return tool->itrace_start (tool, event, sample, machine);default : - ++evlist->stats.nr_unknown_events; + ++stats->nr_unknown_events;return -1 ; } } static int perf_session__deliver_event(struct perf_session *session, + struct events_stats *stats, union perf_event *event, struct perf_sample *sample, struct perf_tool *tool,
@@ -1080,8 +1088,9 @@ static int perf_session__deliver_event(struct perf_session *session,if (ret >0 )return 0 ; -return machines__deliver_event (&session->machines, session->evlist, - event, sample, tool, file_offset); +return machines__deliver_event (&session->machines, stats, + session->evlist, event, sample, + tool, file_offset); } static s64 perf_session__process_user_event(struct perf_session *session,
@@ -1146,7 +1155,8 @@ int perf_session__deliver_synth_event(struct perf_session *session,if (event->header.type >=PERF_RECORD_USER_TYPE_START )return perf_session__process_user_event (session, event,0 ); -return machines__deliver_event (&session->machines, evlist, event, sample, tool,0 ); +return machines__deliver_event (&session->machines, &evlist->stats, + evlist, event, sample, tool,0 ); }static void event_swap (union perf_event *event,bool sample_id_all)
@@ -1214,7 +1224,9 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, }static s64 perf_session__process_event (struct perf_session *session, -union perf_event *event,u64 file_offset) +struct events_stats *stats, +union perf_event *event, +u64 file_offset) {struct perf_evlist *evlist = session->evlist;struct perf_tool *tool = session->tool;
@@ -1227,7 +1239,7 @@ static s64 perf_session__process_event(struct perf_session *session,if (event->header.type >=PERF_RECORD_HEADER_MAX )return -EINVAL ; -events_stats__inc (&evlist->stats, event->header.type); +events_stats__inc (stats, event->header.type);if (event->header.type >=PERF_RECORD_USER_TYPE_START )return perf_session__process_user_event (session, event, file_offset);
@@ -1245,8 +1257,8 @@ static s64 perf_session__process_event(struct perf_session *session,return ret; } -return perf_session__deliver_event (session, event, &sample, tool, - file_offset); +return perf_session__deliver_event (session, stats, event, + &sample, tool, file_offset); }void perf_event_header__bswap (struct perf_event_header *hdr)
@@ -1274,9 +1286,9 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *sereturn thread; } -static void perf_session__warn_about_errors (const struct perf_session *session) +static void perf_session__warn_about_errors (const struct perf_session *session, +const struct events_stats *stats) { -const struct events_stats *stats = &session->evlist->stats;const struct ordered_events *oe = &session->ordered_events; if (session->tool->lost == perf_event__process_lost &&
@@ -1326,6 +1338,7 @@ volatile int session_done;static int __perf_session__process_pipe_events (struct perf_session *session) {struct ordered_events *oe = &session->ordered_events; +struct events_stats *stats = &session->evlist->stats;struct perf_tool *tool = session->tool;int fd =perf_data_file__fd (session->file);union perf_event *event;
@@ -1390,7 +1403,8 @@ static int __perf_session__process_pipe_events(struct perf_session *session) } } -if ((skip =perf_session__process_event (session, event, head)) <0 ) { +if ((skip =perf_session__process_event (session, stats, event, + head)) <0 ) {pr_err ("%#" PRIx64" [%#x]: failed to process type: %d\n" , head, event->header.size, event->header.type); err = -EINVAL ;
@@ -1412,7 +1426,7 @@ static int __perf_session__process_pipe_events(struct perf_session *session) err =auxtrace__flush_events (session, tool); out_err:free (buf); -perf_session__warn_about_errors (session); +perf_session__warn_about_errors (session, stats);ordered_events__free (&session->ordered_events);auxtrace__free_events (session);return err;
@@ -1459,6 +1473,7 @@ fetch_mmaped_event(struct perf_session *session,#endif static int __perf_session__process_events (struct perf_session *session, +struct events_stats *stats,u64 data_offset,u64 data_size,u64 file_size) {
@@ -1534,7 +1549,8 @@ static int __perf_session__process_events(struct perf_session *session, size = event->header.size;if (size <sizeof (struct perf_event_header ) || - (skip =perf_session__process_event (session, event, file_pos)) <0 ) { + (skip =perf_session__process_event (session, stats, event, + file_pos)) <0 ) {pr_err ("%#" PRIx64" [%#x]: failed to process type: %d\n" , file_offset + head, event->header.size, event->header.type);
@@ -1575,6 +1591,7 @@ static int __perf_session__process_indexed_events(struct perf_session *session)struct perf_data_file *file = session->file;struct perf_tool *tool = session->tool;u64 size =perf_data_file__size (file); +struct events_stats *stats = &session->evlist->stats;int err =0 , i;for (i =0 ; i < (int )session->header.nr_index; i++) {
@@ -1591,13 +1608,14 @@ static int __perf_session__process_indexed_events(struct perf_session *session)if (i >0 ) tool->ordered_events = false; - err =__perf_session__process_events (session, idx->offset, + err =__perf_session__process_events (session, stats, + idx->offset, idx->size, size);if (err <0 )break ; } -perf_session__warn_about_errors (session); +perf_session__warn_about_errors (session, stats);return err; }
@@ -1605,6 +1623,7 @@ int perf_session__process_events(struct perf_session *session) {struct perf_data_file *file = session->file;u64 size =perf_data_file__size (file); +struct events_stats *stats = &session->evlist->stats;int err;if (perf_session__register_idle_thread (session) ==NULL )
@@ -1615,12 +1634,12 @@ int perf_session__process_events(struct perf_session *session)if (perf_has_index)return __perf_session__process_indexed_events (session); - err =__perf_session__process_events (session, + err =__perf_session__process_events (session, stats, session->header.data_offset, session->header.data_size, size); -perf_session__warn_about_errors (session); +perf_session__warn_about_errors (session, stats);return err; }
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 31/40 perf hists: Pass hists struct to hist_entry_iter struct
In reply to Namhyung KimThis is a preparation for perf report multi-thread support. When multi-thread is enable, each thread will have its own hists during the sample processing.
--- tools/perf/builtin-report.c | 1 + tools/perf/builtin-top.c | 1 + tools/perf/tests/hists_cumulate.c | 1 + tools/perf/tests/hists_filter.c | 1 + tools/perf/tests/hists_output.c | 1 + tools/perf/util/hist.c | 20 +++++++------------- tools/perf/util/hist.h | 1 + 7 files changed, 13 insertions(+), 13 deletions(-)
tools/perf/builtin-report.c +1 -0
@@ -140,6 +140,7 @@ static int process_sample_event(struct perf_tool *tool,struct addr_location al;struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = sample, .hide_unresolved = rep->hide_unresolved, .add_entry_cb = hist_iter__report_callback,
tools/perf/builtin-top.c +1 -0
@@ -776,6 +776,7 @@ static void perf_event__process_sample(struct perf_tool *tool,struct hists *hists =evsel__hists (evsel);struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = sample, .add_entry_cb = hist_iter__top_callback, };
tools/perf/tests/hists_cumulate.c +1 -0
@@ -88,6 +88,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) };struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = &sample, .hide_unresolved = false, };
tools/perf/tests/hists_filter.c +1 -0
@@ -64,6 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, };struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false,
tools/perf/tests/hists_output.c +1 -0
@@ -58,6 +58,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) };struct hist_entry_iter iter = { .evsel = evsel, + .hists =evsel__hists (evsel), .sample = &sample, .ops = &hist_iter_normal, .hide_unresolved = false,
tools/perf/util/hist.c +7 -13
@@ -505,7 +505,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *alu64 cost;struct mem_info *mi = iter->priv;struct perf_sample *sample = iter->sample; -struct hists *hists =evsel__hists (iter->evsel); +struct hists *hists = iter->hists;struct hist_entry *he;if (mi ==NULL )
@@ -535,8 +535,7 @@ static int iter_finish_mem_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { - struct perf_evsel *evsel = iter->evsel; -struct hists *hists =evsel__hists (evsel); + struct hists *hists = iter->hists;struct hist_entry *he = iter->he;int err = -EINVAL ;
@@ -608,8 +607,7 @@ static int iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) { struct branch_info *bi; -struct perf_evsel *evsel = iter->evsel; -struct hists *hists =evsel__hists (evsel); +struct hists *hists = iter->hists;struct hist_entry *he =NULL ;int i = iter->curr;int err =0 ;
@@ -656,11 +654,10 @@ iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,static int iter_add_single_normal_entry (struct hist_entry_iter *iter,struct addr_location *al) { -struct perf_evsel *evsel = iter->evsel;struct perf_sample *sample = iter->sample;struct hist_entry *he; - he =__hists__add_entry (evsel__hists (evsel), al, iter->parent,NULL ,NULL , + he =__hists__add_entry (iter->hists, al, iter->parent,NULL ,NULL , sample->period, sample->weight, sample->transaction, sample->time, true);if (he ==NULL )
@@ -675,7 +672,6 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) {struct hist_entry *he = iter->he; -struct perf_evsel *evsel = iter->evsel;struct perf_sample *sample = iter->sample;if (he ==NULL )
@@ -683,7 +679,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, iter->he =NULL ; -hists__inc_nr_samples (evsel__hists (evsel), he->filtered); +hists__inc_nr_samples (iter->hists, he->filtered);return hist_entry__append_callchain (he, sample); }
@@ -715,8 +711,7 @@ static int iter_add_single_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct perf_evsel *evsel = iter->evsel; -struct hists *hists =evsel__hists (evsel); + struct hists *hists = iter->hists;struct perf_sample *sample = iter->sample;struct hist_entry **he_cache = iter->priv;struct hist_entry *he;
@@ -761,7 +756,6 @@ static int iter_add_next_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct perf_evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample;struct hist_entry **he_cache = iter->priv;struct hist_entry *he;
@@ -795,7 +789,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, } } - he =__hists__add_entry (evsel__hists (evsel), al, iter->parent,NULL ,NULL , + he =__hists__add_entry (iter->hists, al, iter->parent,NULL ,NULL , sample->period, sample->weight, sample->transaction, sample->time, false);if (he ==NULL )
tools/perf/util/hist.h +1 -0
@@ -86,6 +86,7 @@ struct hist_entry_iter {bool hide_unresolved; +struct hists *hists;struct perf_evsel *evsel;struct perf_sample *sample;struct hist_entry *he;
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 32/40 perf tools: Move BUILD_ID_SIZE definition to perf.h
In reply to Namhyung KimThe util/event.h includes util/build-id.h only for BUILD_ID_SIZE. This is a problem when I include util/event.h from util/tool.h which is also included by util/build-id.h since it now makes a circular dependency resulting in incomplete type error.
--- tools/perf/perf.h | 1 + tools/perf/util/build-id.h | 2 -- tools/perf/util/dso.h | 1 + tools/perf/util/event.h | 1 - 4 files changed, 2 insertions(+), 3 deletions(-)
tools/perf/perf.h +1 -0
@@ -30,6 +30,7 @@ static inline unsigned long long rdclock(void) }#define MAX_NR_CPUS 1024 +#define BUILD_ID_SIZE 20extern const char *input_name;extern bool perf_host, perf_guest;
tools/perf/util/build-id.h +0 -2
@@ -1,8 +1,6 @@#ifndef PERF_BUILD_ID_H_ #define PERF_BUILD_ID_H_ 1 -#define BUILD_ID_SIZE 20 -#include "tool.h" #include "strlist.h" #include <linux/types.h>
tools/perf/util/dso.h +1 -0
@@ -7,6 +7,7 @@#include <linux/types.h> #include <linux/bitops.h> #include "map.h" +#include "perf.h" #include "build-id.h" enum dso_binary_type {
tools/perf/util/event.h +0 -1
@@ -6,7 +6,6 @@#include "../perf.h" #include "map.h" -#include "build-id.h" #include "perf_regs.h" struct mmap_event {
Signature · 1 line
2.4.0
Namhyung Kim
maintainerPATCH 33/40 perf session: Separate struct machines from session
In reply to Namhyung KimWith multi-thread report, separate sessions can be passed to each thread, in this case we should keep a single machine state for all struct sessions. Separate machines and have a pointer in sessions.
--- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-kmem.c | 10 +++++----- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-record.c | 4 ++-- tools/perf/builtin-report.c | 4 ++-- tools/perf/builtin-top.c | 8 ++++---- tools/perf/builtin-trace.c | 2 +- tools/perf/util/build-id.c | 16 ++++++++-------- tools/perf/util/session.c | 36 +++++++++++++++++++++--------------- tools/perf/util/session.h | 6 +++--- 10 files changed, 48 insertions(+), 42 deletions(-)
tools/perf/builtin-annotate.c +1 -1
@@ -196,7 +196,7 @@ static int __cmd_annotate(struct perf_annotate *ann)struct perf_evsel *pos;u64 total_nr_samples; -machines__set_symbol_filter (&session->machines, symbol__annotate_init); +machines__set_symbol_filter (session->machines, symbol__annotate_init);if (ann->cpu_list) { ret = perf_session__cpu_bitmap(session, ann->cpu_list,
tools/perf/builtin-kmem.c +5 -5
@@ -316,7 +316,7 @@ static int build_alloc_func_list(void)struct symbol *sym;struct rb_node *node;struct alloc_func *func; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;regex_t alloc_func_regex;const char pattern[] ="^_?_?(alloc|get_free|get_zeroed)_pages?" ;
@@ -366,7 +366,7 @@ static int build_alloc_func_list(void)static u64 find_callsite (struct perf_evsel *evsel,struct perf_sample *sample) {struct addr_location al; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;struct callchain_cursor_node *node;if (alloc_func_list ==NULL ) {
@@ -949,7 +949,7 @@ static void __print_slab_result(struct rb_root *root, int n_lines, int is_caller) {struct rb_node *next; -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;printf ("%.105s\n" , graph_dotted_line);printf (" %-34s |" , is_caller ?"Callsite" :"Alloc Ptr" );
@@ -1010,7 +1010,7 @@ static const char * const migrate_type_str[] = {static void __print_page_alloc_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_alloc_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;const char *format;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);
@@ -1060,7 +1060,7 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)static void __print_page_caller_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_caller_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);printf ("\n%.105s\n" , graph_dotted_line);
tools/perf/builtin-kvm.c +1 -1
@@ -1392,7 +1392,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, kvm->session->evlist = kvm->evlist;perf_session__set_id_hdr_size (kvm->session);ordered_events__set_copy_on_queue (&kvm->session->ordered_events, true); -machine__synthesize_threads (&kvm->session->machines.host, &kvm->opts.target, +machine__synthesize_threads (&kvm->session->machines->host, &kvm->opts.target, kvm->evlist->threads, false); err =kvm_live_open_events (kvm);if (err)
tools/perf/builtin-record.c +2 -2
@@ -684,7 +684,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } - machine = &session->machines.host; + machine = &session->machines->host;if (file->is_pipe) { err = perf_event__synthesize_attrs(tool, session,
@@ -735,7 +735,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) "Check /proc/modules permission or run as root.\n");if (perf_guest) { -machines__process_guests (&session->machines, +machines__process_guests (session->machines, perf_event__synthesize_guest_os, tool); }
tools/perf/builtin-report.c +2 -2
@@ -353,7 +353,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,static void report__warn_kptr_restrict (const struct report *rep) { -struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION ]; +struct map *kernel_map = rep->session->machines->host.vmlinux_maps[MAP__FUNCTION ];struct kmap *kernel_kmap = kernel_map ?map__kmap (kernel_map) :NULL ; if (kernel_map == NULL ||
@@ -845,7 +845,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)*/ if (ui__has_annotation ()) { symbol_conf.priv_size =sizeof (struct annotation ); -machines__set_symbol_filter (&session->machines, +machines__set_symbol_filter (session->machines, symbol__annotate_init);/* * For searching by name on the "Browse map details".
tools/perf/builtin-top.c +4 -4
@@ -828,13 +828,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) ++top->us_samples;if (top->hide_user_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_KERNEL : ++top->kernel_samples;if (top->hide_kernel_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_GUEST_KERNEL : ++top->guest_kernel_samples;
@@ -939,7 +939,7 @@ static int __cmd_top(struct perf_top *top)if (top->session ==NULL )return -1 ; -machines__set_symbol_filter (&top->session->machines, symbol_filter); +machines__set_symbol_filter (top->session->machines, symbol_filter);if (!objdump_path) { ret =perf_session_env__lookup_objdump (&top->session->header.env);
@@ -951,7 +951,7 @@ static int __cmd_top(struct perf_top *top)if (ret) goto out_delete; -machine__synthesize_threads (&top->session->machines.host, &opts->target, +machine__synthesize_threads (&top->session->machines->host, &opts->target, top->evlist->threads, false); ret =perf_top__start_counters (top);if (ret)
tools/perf/builtin-trace.c +1 -1
@@ -2418,7 +2418,7 @@ static int trace__replay(struct trace *trace)if (symbol__init (&session->header.env) <0 ) goto out; - trace->host = &session->machines.host; + trace->host = &session->machines->host; err =perf_session__set_tracepoints_handlers (session, handlers);if (err)
tools/perf/util/build-id.c +8 -8
@@ -221,12 +221,12 @@ static int machine__write_buildid_table(struct machine *machine, int fd)int perf_session__write_buildid_table (struct perf_session *session,int fd) {struct rb_node *nd; -int err =machine__write_buildid_table (&session->machines.host, fd); +int err =machine__write_buildid_table (&session->machines->host, fd);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__write_buildid_table (pos, fd);if (err)
@@ -261,11 +261,11 @@ int dsos__hit_all(struct perf_session *session)struct rb_node *nd;int err; - err =machine__hit_all_dsos (&session->machines.host); + err =machine__hit_all_dsos (&session->machines->host);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__hit_all_dsos (pos);
@@ -509,9 +509,9 @@ int perf_session__cache_build_ids(struct perf_session *session)if (mkdir (buildid_dir,0755 ) !=0 && errno !=EEXIST )return -1 ; - ret =machine__cache_build_ids (&session->machines.host); + ret =machine__cache_build_ids (&session->machines->host); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__cache_build_ids (pos); }
@@ -530,9 +530,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits)bool perf_session__read_build_ids (struct perf_session *session,bool with_hits) {struct rb_node *nd; -bool ret =machine__read_build_ids (&session->machines.host, with_hits); +bool ret =machine__read_build_ids (&session->machines->host, with_hits); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__read_build_ids (pos, with_hits); }
tools/perf/util/session.c +21 -15
@@ -58,21 +58,21 @@ void perf_session__set_id_hdr_size(struct perf_session *session) {u16 id_hdr_size =perf_evlist__id_hdr_size (session->evlist); -machines__set_id_hdr_size (&session->machines, id_hdr_size); +machines__set_id_hdr_size (session->machines, id_hdr_size); }int perf_session__create_kernel_maps (struct perf_session *session) { -int ret =machine__create_kernel_maps (&session->machines.host); +int ret =machine__create_kernel_maps (&session->machines->host);if (ret >=0 ) - ret =machines__create_guest_kernel_maps (&session->machines); + ret =machines__create_guest_kernel_maps (session->machines);return ret; }static void perf_session__destroy_kernel_maps (struct perf_session *session) { -machines__destroy_kernel_maps (&session->machines); +machines__destroy_kernel_maps (session->machines); }static bool perf_session__has_comm_exec (struct perf_session *session)
@@ -91,7 +91,7 @@ static void perf_session__set_comm_exec(struct perf_session *session) {bool comm_exec =perf_session__has_comm_exec (session); -machines__set_comm_exec (&session->machines, comm_exec); +machines__set_comm_exec (session->machines, comm_exec); } static int ordered_events__deliver_event(struct ordered_events *oe,
@@ -123,7 +123,12 @@ struct perf_session *perf_session__new(struct perf_data_file *file, session->repipe = repipe; session->tool = tool;INIT_LIST_HEAD (&session->auxtrace_index); -machines__init (&session->machines); + + session->machines =malloc (sizeof (*session->machines)); +if (!session->machines) + goto out_delete; + +machines__init (session->machines);ordered_events__init (&session->ordered_events, ordered_events__deliver_event);if (file) {
@@ -168,7 +173,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,static void perf_session__delete_threads (struct perf_session *session) { -machine__delete_threads (&session->machines.host); +machine__delete_threads (&session->machines->host); }static void perf_session_env__delete (struct perf_session_env *env)
@@ -194,7 +199,8 @@ void perf_session__delete(struct perf_session *session)perf_session__destroy_kernel_maps (session);perf_session__delete_threads (session);perf_session_env__delete (&session->header.env); -machines__exit (&session->machines); +machines__exit (session->machines); +free (session->machines);if (session->file)perf_data_file__close (session->file);free (session->header.index);
@@ -1088,7 +1094,7 @@ static int perf_session__deliver_event(struct perf_session *session,if (ret >0 )return 0 ; -return machines__deliver_event (&session->machines, stats, +return machines__deliver_event (session->machines, stats, session->evlist, event, sample, tool, file_offset); }
@@ -1155,7 +1161,7 @@ int perf_session__deliver_synth_event(struct perf_session *session,if (event->header.type >=PERF_RECORD_USER_TYPE_START )return perf_session__process_user_event (session, event,0 ); -return machines__deliver_event (&session->machines, &evlist->stats, +return machines__deliver_event (session->machines, &evlist->stats, evlist, event, sample, tool,0 ); }
@@ -1270,14 +1276,14 @@ void perf_event_header__bswap(struct perf_event_header *hdr)struct thread *perf_session__findnew (struct perf_session *session,pid_t pid) { -return machine__findnew_thread (&session->machines.host,-1 , pid); +return machine__findnew_thread (&session->machines->host,-1 , pid); }static struct thread *perf_session__register_idle_thread (struct perf_session *session) {struct thread *thread; - thread =machine__findnew_thread (&session->machines.host,0 ,0 ); + thread =machine__findnew_thread (&session->machines->host,0 ,0 );if (thread ==NULL ||thread__set_comm (thread,"swapper" ,0 )) {pr_err ("problem inserting idle task.\n" ); thread =NULL ;
@@ -1692,13 +1698,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps,size_t perf_session__fprintf_dsos (struct perf_session *session,FILE *fp) { -return machines__fprintf_dsos (&session->machines, fp); +return machines__fprintf_dsos (session->machines, fp); }size_t perf_session__fprintf_dsos_buildid (struct perf_session *session,FILE *fp,bool (skip )(struct dso *dso,int parm),int parm) { -return machines__fprintf_dsos_buildid (&session->machines, fp, skip, parm); +return machines__fprintf_dsos_buildid (session->machines, fp, skip, parm); }size_t perf_session__fprintf_nr_events (struct perf_session *session,FILE *fp)
@@ -1721,7 +1727,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)* FIXME: Here we have to actually print all the machines in this * session, not just the host... */ -return machine__fprintf (&session->machines.host, fp); +return machine__fprintf (&session->machines->host, fp); } struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
tools/perf/util/session.h +3 -3
@@ -20,7 +20,7 @@ struct itrace_synth_opts;struct perf_session {struct perf_header header; -struct machines machines; +struct machines *machines;struct perf_evlist *evlist;struct auxtrace *auxtrace;struct itrace_synth_opts *itrace_synth_opts;
@@ -79,13 +79,13 @@ void perf_session__set_id_hdr_size(struct perf_session *session);static inline struct machine *perf_session__find_machine (struct perf_session *session,pid_t pid) { -return machines__find (&session->machines, pid); +return machines__find (session->machines, pid); }static inline struct machine *perf_session__findnew_machine (struct perf_session *session,pid_t pid) { -return machines__findnew (&session->machines, pid); +return machines__findnew (session->machines, pid); }struct thread *perf_session__findnew (struct perf_session *session,pid_t pid);
Signature · 1 line
2.4.0
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 33/40] perf session: Separate struct machines from session
Em Mon, May 18, 2015 at 09:30:48AM +0900, Namhyung Kim escreveu:
Quoted 3 lines
With multi-thread report, separate sessions can be passed to each thread, in this case we should keep a single machine state for all struct sessions. Separate machines and have a pointer in sessions.
I had to look at all the patch to semi-figure this out, i.e. you said it should be separated from 'perf_session', agreed.
But who will create it? How will it be passed to the perf_session instances?
Most of the patch is making session->machines be turned into a pointer, but the meat, i.e. who creates it, is unclear, I see a malloc in perf_session__new(), where I was kinda expecting that a higer layer, perhaps in struct tool? Would create the list of all machines (struct machines) and then pass it to multiple perf_session__new() calls.
But then perf_session__delete() calls 'free(session->machines)', huh?
- Arnaldo
Quoted 404 lines
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-kmem.c | 10 +++++----- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-record.c | 4 ++-- tools/perf/builtin-report.c | 4 ++-- tools/perf/builtin-top.c | 8 ++++---- tools/perf/builtin-trace.c | 2 +- tools/perf/util/build-id.c | 16 ++++++++-------- tools/perf/util/session.c | 36 +++++++++++++++++++++--------------- tools/perf/util/session.h | 6 +++--- 10 files changed, 48 insertions(+), 42 deletions(-)tools/perf/builtin-annotate.c +1
-1@@ -196,7 +196,7 @@ static int __cmd_annotate(struct perf_annotate *ann)struct perf_evsel *pos;u64 total_nr_samples; -machines__set_symbol_filter (&session->machines, symbol__annotate_init); +machines__set_symbol_filter (session->machines, symbol__annotate_init);if (ann->cpu_list) { ret = perf_session__cpu_bitmap(session, ann->cpu_list,tools/perf/builtin-kmem.c +5
-5@@ -316,7 +316,7 @@ static int build_alloc_func_list(void)struct symbol *sym;struct rb_node *node;struct alloc_func *func; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;regex_t alloc_func_regex;const char pattern[] ="^_?_?(alloc|get_free|get_zeroed)_pages?" ;@@ -366,7 +366,7 @@ static int build_alloc_func_list(void)static u64 find_callsite (struct perf_evsel *evsel,struct perf_sample *sample) {struct addr_location al; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;struct callchain_cursor_node *node;if (alloc_func_list ==NULL ) {@@ -949,7 +949,7 @@ static void __print_slab_result(struct rb_root *root, int n_lines, int is_caller) {struct rb_node *next; -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;printf ("%.105s\n" , graph_dotted_line);printf (" %-34s |" , is_caller ?"Callsite" :"Alloc Ptr" );@@ -1010,7 +1010,7 @@ static const char * const migrate_type_str[] = {static void __print_page_alloc_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_alloc_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;const char *format;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);@@ -1060,7 +1060,7 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)static void __print_page_caller_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_caller_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);printf ("\n%.105s\n" , graph_dotted_line);tools/perf/builtin-kvm.c +1
-1@@ -1392,7 +1392,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, kvm->session->evlist = kvm->evlist;perf_session__set_id_hdr_size (kvm->session);ordered_events__set_copy_on_queue (&kvm->session->ordered_events, true); -machine__synthesize_threads (&kvm->session->machines.host, &kvm->opts.target, +machine__synthesize_threads (&kvm->session->machines->host, &kvm->opts.target, kvm->evlist->threads, false); err =kvm_live_open_events (kvm);if (err)tools/perf/builtin-record.c +2
-2@@ -684,7 +684,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } - machine = &session->machines.host; + machine = &session->machines->host;if (file->is_pipe) { err = perf_event__synthesize_attrs(tool, session,@@ -735,7 +735,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) "Check /proc/modules permission or run as root.\n");if (perf_guest) { -machines__process_guests (&session->machines, +machines__process_guests (session->machines, perf_event__synthesize_guest_os, tool); }tools/perf/builtin-report.c +2
-2@@ -353,7 +353,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,static void report__warn_kptr_restrict (const struct report *rep) { -struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION ]; +struct map *kernel_map = rep->session->machines->host.vmlinux_maps[MAP__FUNCTION ];struct kmap *kernel_kmap = kernel_map ?map__kmap (kernel_map) :NULL ; if (kernel_map == NULL ||@@ -845,7 +845,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)*/ if (ui__has_annotation ()) { symbol_conf.priv_size =sizeof (struct annotation ); -machines__set_symbol_filter (&session->machines, +machines__set_symbol_filter (session->machines, symbol__annotate_init);/* * For searching by name on the "Browse map details". tools/perf/builtin-top.c +4
-4@@ -828,13 +828,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) ++top->us_samples;if (top->hide_user_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_KERNEL : ++top->kernel_samples;if (top->hide_kernel_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_GUEST_KERNEL : ++top->guest_kernel_samples;@@ -939,7 +939,7 @@ static int __cmd_top(struct perf_top *top)if (top->session ==NULL )return -1 ; -machines__set_symbol_filter (&top->session->machines, symbol_filter); +machines__set_symbol_filter (top->session->machines, symbol_filter);if (!objdump_path) { ret =perf_session_env__lookup_objdump (&top->session->header.env);@@ -951,7 +951,7 @@ static int __cmd_top(struct perf_top *top)if (ret) goto out_delete; -machine__synthesize_threads (&top->session->machines.host, &opts->target, +machine__synthesize_threads (&top->session->machines->host, &opts->target, top->evlist->threads, false); ret =perf_top__start_counters (top);if (ret)tools/perf/builtin-trace.c +1
-1@@ -2418,7 +2418,7 @@ static int trace__replay(struct trace *trace)if (symbol__init (&session->header.env) <0 ) goto out; - trace->host = &session->machines.host; + trace->host = &session->machines->host; err =perf_session__set_tracepoints_handlers (session, handlers);if (err)tools/perf/util/build-id.c +8
-8@@ -221,12 +221,12 @@ static int machine__write_buildid_table(struct machine *machine, int fd)int perf_session__write_buildid_table (struct perf_session *session,int fd) {struct rb_node *nd; -int err =machine__write_buildid_table (&session->machines.host, fd); +int err =machine__write_buildid_table (&session->machines->host, fd);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__write_buildid_table (pos, fd);if (err)@@ -261,11 +261,11 @@ int dsos__hit_all(struct perf_session *session)struct rb_node *nd;int err; - err =machine__hit_all_dsos (&session->machines.host); + err =machine__hit_all_dsos (&session->machines->host);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__hit_all_dsos (pos);@@ -509,9 +509,9 @@ int perf_session__cache_build_ids(struct perf_session *session)if (mkdir (buildid_dir,0755 ) !=0 && errno !=EEXIST )return -1 ; - ret =machine__cache_build_ids (&session->machines.host); + ret =machine__cache_build_ids (&session->machines->host); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__cache_build_ids (pos); }@@ -530,9 +530,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits)bool perf_session__read_build_ids (struct perf_session *session,bool with_hits) {struct rb_node *nd; -bool ret =machine__read_build_ids (&session->machines.host, with_hits); +bool ret =machine__read_build_ids (&session->machines->host, with_hits); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__read_build_ids (pos, with_hits); }tools/perf/util/session.c +21
-15@@ -58,21 +58,21 @@ void perf_session__set_id_hdr_size(struct perf_session *session) {u16 id_hdr_size =perf_evlist__id_hdr_size (session->evlist); -machines__set_id_hdr_size (&session->machines, id_hdr_size); +machines__set_id_hdr_size (session->machines, id_hdr_size); }int perf_session__create_kernel_maps (struct perf_session *session) { -int ret =machine__create_kernel_maps (&session->machines.host); +int ret =machine__create_kernel_maps (&session->machines->host);if (ret >=0 ) - ret =machines__create_guest_kernel_maps (&session->machines); + ret =machines__create_guest_kernel_maps (session->machines);return ret; }static void perf_session__destroy_kernel_maps (struct perf_session *session) { -machines__destroy_kernel_maps (&session->machines); +machines__destroy_kernel_maps (session->machines); }static bool perf_session__has_comm_exec (struct perf_session *session)@@ -91,7 +91,7 @@ static void perf_session__set_comm_exec(struct perf_session *session) {bool comm_exec =perf_session__has_comm_exec (session); -machines__set_comm_exec (&session->machines, comm_exec); +machines__set_comm_exec (session->machines, comm_exec); } static int ordered_events__deliver_event(struct ordered_events *oe,@@ -123,7 +123,12 @@ struct perf_session *perf_session__new(struct perf_data_file *file, session->repipe = repipe; session->tool = tool;INIT_LIST_HEAD (&session->auxtrace_index); -machines__init (&session->machines); + + session->machines =malloc (sizeof (*session->machines)); +if (!session->machines) + goto out_delete; + +machines__init (session->machines);ordered_events__init (&session->ordered_events, ordered_events__deliver_event);if (file) {@@ -168,7 +173,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,static void perf_session__delete_threads (struct perf_session *session) { -machine__delete_threads (&session->machines.host); +machine__delete_threads (&session->machines->host); }static void perf_session_env__delete (struct perf_session_env *env)@@ -194,7 +199,8 @@ void perf_session__delete(struct perf_session *session)perf_session__destroy_kernel_maps (session);perf_session__delete_threads (session);perf_session_env__delete (&session->header.env); -machines__exit (&session->machines); +machines__exit (session->machines); +free (session->machines);if (session->file)perf_data_file__close (session->file);free (session->header.index);@@ -1088,7 +1094,7 @@ static int perf_session__deliver_event(struct perf_session *session,if (ret >0 )return 0 ; -return machines__deliver_event (&session->machines, stats, +return machines__deliver_event (session->machines, stats, session->evlist, event, sample, tool, file_offset); }@@ -1155,7 +1161,7 @@ int perf_session__deliver_synth_event(struct perf_session *session,if (event->header.type >=PERF_RECORD_USER_TYPE_START )return perf_session__process_user_event (session, event,0 ); -return machines__deliver_event (&session->machines, &evlist->stats, +return machines__deliver_event (session->machines, &evlist->stats, evlist, event, sample, tool,0 ); }@@ -1270,14 +1276,14 @@ void perf_event_header__bswap(struct perf_event_header *hdr)struct thread *perf_session__findnew (struct perf_session *session,pid_t pid) { -return machine__findnew_thread (&session->machines.host,-1 , pid); +return machine__findnew_thread (&session->machines->host,-1 , pid); }static struct thread *perf_session__register_idle_thread (struct perf_session *session) {struct thread *thread; - thread =machine__findnew_thread (&session->machines.host,0 ,0 ); + thread =machine__findnew_thread (&session->machines->host,0 ,0 );if (thread ==NULL ||thread__set_comm (thread,"swapper" ,0 )) {pr_err ("problem inserting idle task.\n" ); thread =NULL ;@@ -1692,13 +1698,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps,size_t perf_session__fprintf_dsos (struct perf_session *session,FILE *fp) { -return machines__fprintf_dsos (&session->machines, fp); +return machines__fprintf_dsos (session->machines, fp); }size_t perf_session__fprintf_dsos_buildid (struct perf_session *session,FILE *fp,bool (skip )(struct dso *dso,int parm),int parm) { -return machines__fprintf_dsos_buildid (&session->machines, fp, skip, parm); +return machines__fprintf_dsos_buildid (session->machines, fp, skip, parm); }size_t perf_session__fprintf_nr_events (struct perf_session *session,FILE *fp)@@ -1721,7 +1727,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)* FIXME: Here we have to actually print all the machines in this * session, not just the host... */ -return machine__fprintf (&session->machines.host, fp); +return machine__fprintf (&session->machines->host, fp); } struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,tools/perf/util/session.h +3
-3@@ -20,7 +20,7 @@ struct itrace_synth_opts;struct perf_session {struct perf_header header; -struct machines machines; +struct machines *machines;struct perf_evlist *evlist;struct auxtrace *auxtrace;struct itrace_synth_opts *itrace_synth_opts;@@ -79,13 +79,13 @@ void perf_session__set_id_hdr_size(struct perf_session *session);static inline struct machine *perf_session__find_machine (struct perf_session *session,pid_t pid) { -return machines__find (&session->machines, pid); +return machines__find (session->machines, pid); }static inline struct machine *perf_session__findnew_machine (struct perf_session *session,pid_t pid) { -return machines__findnew (&session->machines, pid); +return machines__findnew (session->machines, pid); }struct thread *perf_session__findnew (struct perf_session *session,pid_t pid);Signature · 1 line
2.4.0
Namhyung Kim
maintainerRe: [PATCH 33/40] perf session: Separate struct machines from session
Quoted 11 lines · Arnaldo Carvalho de Melo
Em Mon, May 18, 2015 at 09:30:48AM +0900, Namhyung Kim escreveu:
Quoted 3 lines
With multi-thread report, separate sessions can be passed to each thread, in this case we should keep a single machine state for all struct sessions. Separate machines and have a pointer in sessions.
I had to look at all the patch to semi-figure this out, i.e. you said it should be separated from '
perf_session', agreed.But who will create it? How will it be passed to the
perf_sessioninstances?
Quoted 7 lines
Most of the patch is making
session->machinesbe turned into a pointer, but the meat, i.e. who creates it, is unclear, I see a malloc inperf_session__new(), where I was kinda expecting that a higer layer, perhaps in struct tool? Would create the list of all machines (struct machines) and then pass it to multipleperf_session__new()calls.But then
perf_session__delete()calls 'free(session->machines)', huh?
OK. So, this is what I have in my head:
perf_tool__create_machines(tool) {
tool->machines = malloc();
machines__init(tool->machines);
}
perf_session__new(file, repipe, tool) {
session->machines = tool->machines;
...
}
perf_tool__delete_machines(tool) {
/* call machines-related destructors */
free(tool->machines);
}Are you OK with this?
Thanks,
Namhyung
Quoted 412 lines
- Arnaldo
Quoted 404 lines
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-kmem.c | 10 +++++----- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-record.c | 4 ++-- tools/perf/builtin-report.c | 4 ++-- tools/perf/builtin-top.c | 8 ++++---- tools/perf/builtin-trace.c | 2 +- tools/perf/util/build-id.c | 16 ++++++++-------- tools/perf/util/session.c | 36 +++++++++++++++++++++--------------- tools/perf/util/session.h | 6 +++--- 10 files changed, 48 insertions(+), 42 deletions(-)tools/perf/builtin-annotate.c +1
-1@@ -196,7 +196,7 @@ static int __cmd_annotate(struct perf_annotate *ann)struct perf_evsel *pos;u64 total_nr_samples; -machines__set_symbol_filter (&session->machines, symbol__annotate_init); +machines__set_symbol_filter (session->machines, symbol__annotate_init);if (ann->cpu_list) { ret = perf_session__cpu_bitmap(session, ann->cpu_list,tools/perf/builtin-kmem.c +5
-5@@ -316,7 +316,7 @@ static int build_alloc_func_list(void)struct symbol *sym;struct rb_node *node;struct alloc_func *func; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;regex_t alloc_func_regex;const char pattern[] ="^_?_?(alloc|get_free|get_zeroed)_pages?" ;@@ -366,7 +366,7 @@ static int build_alloc_func_list(void)static u64 find_callsite (struct perf_evsel *evsel,struct perf_sample *sample) {struct addr_location al; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;struct callchain_cursor_node *node;if (alloc_func_list ==NULL ) {@@ -949,7 +949,7 @@ static void __print_slab_result(struct rb_root *root, int n_lines, int is_caller) {struct rb_node *next; -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;printf ("%.105s\n" , graph_dotted_line);printf (" %-34s |" , is_caller ?"Callsite" :"Alloc Ptr" );@@ -1010,7 +1010,7 @@ static const char * const migrate_type_str[] = {static void __print_page_alloc_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_alloc_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;const char *format;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);@@ -1060,7 +1060,7 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)static void __print_page_caller_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_caller_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);printf ("\n%.105s\n" , graph_dotted_line);tools/perf/builtin-kvm.c +1
-1@@ -1392,7 +1392,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, kvm->session->evlist = kvm->evlist;perf_session__set_id_hdr_size (kvm->session);ordered_events__set_copy_on_queue (&kvm->session->ordered_events, true); -machine__synthesize_threads (&kvm->session->machines.host, &kvm->opts.target, +machine__synthesize_threads (&kvm->session->machines->host, &kvm->opts.target, kvm->evlist->threads, false); err =kvm_live_open_events (kvm);if (err)tools/perf/builtin-record.c +2
-2@@ -684,7 +684,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } - machine = &session->machines.host; + machine = &session->machines->host;if (file->is_pipe) { err = perf_event__synthesize_attrs(tool, session,@@ -735,7 +735,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) "Check /proc/modules permission or run as root.\n");if (perf_guest) { -machines__process_guests (&session->machines, +machines__process_guests (session->machines, perf_event__synthesize_guest_os, tool); }tools/perf/builtin-report.c +2
-2@@ -353,7 +353,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,static void report__warn_kptr_restrict (const struct report *rep) { -struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION ]; +struct map *kernel_map = rep->session->machines->host.vmlinux_maps[MAP__FUNCTION ];struct kmap *kernel_kmap = kernel_map ?map__kmap (kernel_map) :NULL ; if (kernel_map == NULL ||@@ -845,7 +845,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)*/ if (ui__has_annotation ()) { symbol_conf.priv_size =sizeof (struct annotation ); -machines__set_symbol_filter (&session->machines, +machines__set_symbol_filter (session->machines, symbol__annotate_init);/* * For searching by name on the "Browse map details". tools/perf/builtin-top.c +4
-4@@ -828,13 +828,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) ++top->us_samples;if (top->hide_user_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_KERNEL : ++top->kernel_samples;if (top->hide_kernel_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_GUEST_KERNEL : ++top->guest_kernel_samples;@@ -939,7 +939,7 @@ static int __cmd_top(struct perf_top *top)if (top->session ==NULL )return -1 ; -machines__set_symbol_filter (&top->session->machines, symbol_filter); +machines__set_symbol_filter (top->session->machines, symbol_filter);if (!objdump_path) { ret =perf_session_env__lookup_objdump (&top->session->header.env);@@ -951,7 +951,7 @@ static int __cmd_top(struct perf_top *top)if (ret) goto out_delete; -machine__synthesize_threads (&top->session->machines.host, &opts->target, +machine__synthesize_threads (&top->session->machines->host, &opts->target, top->evlist->threads, false); ret =perf_top__start_counters (top);if (ret)tools/perf/builtin-trace.c +1
-1@@ -2418,7 +2418,7 @@ static int trace__replay(struct trace *trace)if (symbol__init (&session->header.env) <0 ) goto out; - trace->host = &session->machines.host; + trace->host = &session->machines->host; err =perf_session__set_tracepoints_handlers (session, handlers);if (err)tools/perf/util/build-id.c +8
-8@@ -221,12 +221,12 @@ static int machine__write_buildid_table(struct machine *machine, int fd)int perf_session__write_buildid_table (struct perf_session *session,int fd) {struct rb_node *nd; -int err =machine__write_buildid_table (&session->machines.host, fd); +int err =machine__write_buildid_table (&session->machines->host, fd);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__write_buildid_table (pos, fd);if (err)@@ -261,11 +261,11 @@ int dsos__hit_all(struct perf_session *session)struct rb_node *nd;int err; - err =machine__hit_all_dsos (&session->machines.host); + err =machine__hit_all_dsos (&session->machines->host);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__hit_all_dsos (pos);@@ -509,9 +509,9 @@ int perf_session__cache_build_ids(struct perf_session *session)if (mkdir (buildid_dir,0755 ) !=0 && errno !=EEXIST )return -1 ; - ret =machine__cache_build_ids (&session->machines.host); + ret =machine__cache_build_ids (&session->machines->host); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__cache_build_ids (pos); }@@ -530,9 +530,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits)bool perf_session__read_build_ids (struct perf_session *session,bool with_hits) {struct rb_node *nd; -bool ret =machine__read_build_ids (&session->machines.host, with_hits); +bool ret =machine__read_build_ids (&session->machines->host, with_hits); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__read_build_ids (pos, with_hits); }tools/perf/util/session.c +21
-15@@ -58,21 +58,21 @@ void perf_session__set_id_hdr_size(struct perf_session *session) {u16 id_hdr_size =perf_evlist__id_hdr_size (session->evlist); -machines__set_id_hdr_size (&session->machines, id_hdr_size); +machines__set_id_hdr_size (session->machines, id_hdr_size); }int perf_session__create_kernel_maps (struct perf_session *session) { -int ret =machine__create_kernel_maps (&session->machines.host); +int ret =machine__create_kernel_maps (&session->machines->host);if (ret >=0 ) - ret =machines__create_guest_kernel_maps (&session->machines); + ret =machines__create_guest_kernel_maps (session->machines);return ret; }static void perf_session__destroy_kernel_maps (struct perf_session *session) { -machines__destroy_kernel_maps (&session->machines); +machines__destroy_kernel_maps (session->machines); }static bool perf_session__has_comm_exec (struct perf_session *session)@@ -91,7 +91,7 @@ static void perf_session__set_comm_exec(struct perf_session *session) {bool comm_exec =perf_session__has_comm_exec (session); -machines__set_comm_exec (&session->machines, comm_exec); +machines__set_comm_exec (session->machines, comm_exec); } static int ordered_events__deliver_event(struct ordered_events *oe,@@ -123,7 +123,12 @@ struct perf_session *perf_session__new(struct perf_data_file *file, session->repipe = repipe; session->tool = tool;INIT_LIST_HEAD (&session->auxtrace_index); -machines__init (&session->machines); + + session->machines =malloc (sizeof (*session->machines)); +if (!session->machines) + goto out_delete; + +machines__init (session->machines);ordered_events__init (&session->ordered_events, ordered_events__deliver_event);if (file) {@@ -168,7 +173,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,static void perf_session__delete_threads (struct perf_session *session) { -machine__delete_threads (&session->machines.host); +machine__delete_threads (&session->machines->host); }static void perf_session_env__delete (struct perf_session_env *env)@@ -194,7 +199,8 @@ void perf_session__delete(struct perf_session *session)perf_session__destroy_kernel_maps (session);perf_session__delete_threads (session);perf_session_env__delete (&session->header.env); -machines__exit (&session->machines); +machines__exit (session->machines); +free (session->machines);if (session->file)perf_data_file__close (session->file);free (session->header.index);@@ -1088,7 +1094,7 @@ static int perf_session__deliver_event(struct perf_session *session,if (ret >0 )return 0 ; -return machines__deliver_event (&session->machines, stats, +return machines__deliver_event (session->machines, stats, session->evlist, event, sample, tool, file_offset); }@@ -1155,7 +1161,7 @@ int perf_session__deliver_synth_event(struct perf_session *session,if (event->header.type >=PERF_RECORD_USER_TYPE_START )return perf_session__process_user_event (session, event,0 ); -return machines__deliver_event (&session->machines, &evlist->stats, +return machines__deliver_event (session->machines, &evlist->stats, evlist, event, sample, tool,0 ); }@@ -1270,14 +1276,14 @@ void perf_event_header__bswap(struct perf_event_header *hdr)struct thread *perf_session__findnew (struct perf_session *session,pid_t pid) { -return machine__findnew_thread (&session->machines.host,-1 , pid); +return machine__findnew_thread (&session->machines->host,-1 , pid); }static struct thread *perf_session__register_idle_thread (struct perf_session *session) {struct thread *thread; - thread =machine__findnew_thread (&session->machines.host,0 ,0 ); + thread =machine__findnew_thread (&session->machines->host,0 ,0 );if (thread ==NULL ||thread__set_comm (thread,"swapper" ,0 )) {pr_err ("problem inserting idle task.\n" ); thread =NULL ;@@ -1692,13 +1698,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps,size_t perf_session__fprintf_dsos (struct perf_session *session,FILE *fp) { -return machines__fprintf_dsos (&session->machines, fp); +return machines__fprintf_dsos (session->machines, fp); }size_t perf_session__fprintf_dsos_buildid (struct perf_session *session,FILE *fp,bool (skip )(struct dso *dso,int parm),int parm) { -return machines__fprintf_dsos_buildid (&session->machines, fp, skip, parm); +return machines__fprintf_dsos_buildid (session->machines, fp, skip, parm); }size_t perf_session__fprintf_nr_events (struct perf_session *session,FILE *fp)@@ -1721,7 +1727,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)* FIXME: Here we have to actually print all the machines in this * session, not just the host... */ -return machine__fprintf (&session->machines.host, fp); +return machine__fprintf (&session->machines->host, fp); } struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,tools/perf/util/session.h +3
-3@@ -20,7 +20,7 @@ struct itrace_synth_opts;struct perf_session {struct perf_header header; -struct machines machines; +struct machines *machines;struct perf_evlist *evlist;struct auxtrace *auxtrace;struct itrace_synth_opts *itrace_synth_opts;@@ -79,13 +79,13 @@ void perf_session__set_id_hdr_size(struct perf_session *session);static inline struct machine *perf_session__find_machine (struct perf_session *session,pid_t pid) { -return machines__find (&session->machines, pid); +return machines__find (session->machines, pid); }static inline struct machine *perf_session__findnew_machine (struct perf_session *session,pid_t pid) { -return machines__findnew (&session->machines, pid); +return machines__findnew (session->machines, pid); }struct thread *perf_session__findnew (struct perf_session *session,pid_t pid);Signature · 1 line
2.4.0--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 33/40] perf session: Separate struct machines from session
Em Tue, May 19, 2015 at 04:28:15PM +0900, Namhyung Kim escreveu:
Quoted 18 lines · Arnaldo Carvalho de Melo
Quoted 17 lines
Em Mon, May 18, 2015 at 09:30:48AM +0900, Namhyung Kim escreveu:
Quoted 3 lines
With multi-thread report, separate sessions can be passed to each thread, in this case we should keep a single machine state for all struct sessions. Separate machines and have a pointer in sessions.
I had to look at all the patch to semi-figure this out, i.e. you said it should be separated from '
perf_session', agreed.But who will create it? How will it be passed to the
perf_sessioninstances?Most of the patch is making
session->machinesbe turned into a pointer, but the meat, i.e. who creates it, is unclear, I see a malloc inperf_session__new(), where I was kinda expecting that a higer layer, perhaps in struct tool? Would create the list of all machines (struct machines) and then pass it to multipleperf_session__new()calls.
Quoted 8 lines
Quoted 1 line
But then
perf_session__delete()calls 'free(session->machines)', huh?OK. So, this is what I have in my head:
perf_tool__create_machines(tool) { tool->machines = malloc(); machines__init(tool->machines); }
Probably, but then in this case you would call machines__new(), that does the malloc and init.
Quoted 5 lines
perf_session__new(file, repipe, tool) {
session->machines = tool->machines;
...
}That could be ok.
Quoted 5 lines
perf_tool__delete_machines(tool) {
/* call machines-related destructors */
free(tool->machines);
}That would be machines__delete(tool->machines), that calls machine__exit() and then does the free.
- Arnaldo
Namhyung Kim
maintainerRe: [PATCH 33/40] perf session: Separate struct machines from session
Quoted 41 lines · Arnaldo Carvalho de Melo
Em Tue, May 19, 2015 at 04:28:15PM +0900, Namhyung Kim escreveu:
Quoted 18 lines · Arnaldo Carvalho de Melo
Quoted 17 lines
Em Mon, May 18, 2015 at 09:30:48AM +0900, Namhyung Kim escreveu:
Quoted 3 lines
With multi-thread report, separate sessions can be passed to each thread, in this case we should keep a single machine state for all struct sessions. Separate machines and have a pointer in sessions.
I had to look at all the patch to semi-figure this out, i.e. you said it should be separated from '
perf_session', agreed.But who will create it? How will it be passed to the
perf_sessioninstances?Most of the patch is making
session->machinesbe turned into a pointer, but the meat, i.e. who creates it, is unclear, I see a malloc inperf_session__new(), where I was kinda expecting that a higer layer, perhaps in struct tool? Would create the list of all machines (struct machines) and then pass it to multipleperf_session__new()calls.Quoted 8 lines
Quoted 1 line
But then
perf_session__delete()calls 'free(session->machines)', huh?OK. So, this is what I have in my head:
perf_tool__create_machines(tool) { tool->machines = malloc(); machines__init(tool->machines); }Probably, but then in this case you would call
machines__new(), that does the malloc and init.Quoted 5 lines
perf_session__new(file, repipe, tool) { session->machines = tool->machines; ... }That could be ok.
Quoted 1 line
Quoted 7 lines
Quoted 4 lines
perf_tool__delete_machines(tool) { /* call machines-related destructors */ free(tool->machines); }That would be
machines__delete(tool->machines), that callsmachine__exit()and then does the free.
Right. I'll change it this way.
Thanks,
Namhyung
Namhyung Kim
maintainercover perf session: Separate struct machines from session
Hi Arnaldo,
This is what I have now for the earlier discussion [1]. I just used machines__new/delete functions directly whenever a session is created/deleted. As it touches many callsites, and it needs bit more caring about the error path, you may want to split the first patch for each command. In any case, pleaes let me know what do you think.
Note that this patchset bases on my (slightely modified) previous multi-thread patchset so will not be applied to the current perf/core. I'll rebase and send out after we conclude how to deal with it.
Thanks,
Namhyung
[1] https://lkml.org/lkml/2015/5/19/1062
Namhyung Kim (2):
perf tools: Introduce machines__new/delete() perf session: Separate struct machines from session
tools/perf/builtin-annotate.c | 7 ++++++- tools/perf/builtin-buildid-cache.c | 14 ++++++++++++-- tools/perf/builtin-buildid-list.c | 16 +++++++++++++--- tools/perf/builtin-diff.c | 16 ++++++++++++++-- tools/perf/builtin-evlist.c | 18 +++++++++++++++--- tools/perf/builtin-inject.c | 5 +++++ tools/perf/builtin-kmem.c | 14 +++++++++----- tools/perf/builtin-kvm.c | 14 +++++++++++++- tools/perf/builtin-lock.c | 7 ++++++- tools/perf/builtin-mem.c | 12 +++++++++--- tools/perf/builtin-record.c | 14 ++++++++++---- tools/perf/builtin-report.c | 13 ++++++++++--- tools/perf/builtin-sched.c | 8 +++++++- tools/perf/builtin-script.c | 32 ++++++++++++++++++++++++-------- tools/perf/builtin-timechart.c | 12 +++++++++--- tools/perf/builtin-top.c | 23 ++++++++++++++++------- tools/perf/builtin-trace.c | 10 ++++++++-- tools/perf/util/build-id.c | 16 ++++++++-------- tools/perf/util/data-convert-bt.c | 8 +++++++- tools/perf/util/intel-bts.c | 2 +- tools/perf/util/intel-pt.c | 2 +- tools/perf/util/machine.c | 15 +++++++++++++++ tools/perf/util/machine.h | 3 +++ tools/perf/util/session.c | 30 +++++++++++++++--------------- tools/perf/util/session.h | 6 +++--- tools/perf/util/tool.h | 2 ++ 26 files changed, 241 insertions(+), 78 deletions(-)
Signature · 1 line
2.4.1
Namhyung Kim
maintainerPATCH 1/2 perf tools: Introduce machines__new/delete()
Create and maintain struct machines in struct perf_tool and pass it to struct perf_session. This is a preparation of sharing machines among sessions in case of multi-thread report.
--- tools/perf/builtin-annotate.c | 5 +++++ tools/perf/builtin-buildid-cache.c | 14 ++++++++++++-- tools/perf/builtin-buildid-list.c | 16 +++++++++++++--- tools/perf/builtin-diff.c | 16 ++++++++++++++-- tools/perf/builtin-evlist.c | 18 +++++++++++++++--- tools/perf/builtin-inject.c | 5 +++++ tools/perf/builtin-kmem.c | 4 ++++ tools/perf/builtin-kvm.c | 12 ++++++++++++ tools/perf/builtin-lock.c | 7 ++++++- tools/perf/builtin-mem.c | 12 +++++++++--- tools/perf/builtin-record.c | 8 +++++++- tools/perf/builtin-report.c | 9 ++++++++- tools/perf/builtin-sched.c | 8 +++++++- tools/perf/builtin-script.c | 32 ++++++++++++++++++++++++-------- tools/perf/builtin-timechart.c | 12 +++++++++--- tools/perf/builtin-top.c | 15 ++++++++++++--- tools/perf/builtin-trace.c | 8 +++++++- tools/perf/util/data-convert-bt.c | 8 +++++++- tools/perf/util/machine.c | 15 +++++++++++++++ tools/perf/util/machine.h | 3 +++ tools/perf/util/tool.h | 2 ++ 21 files changed, 196 insertions(+), 33 deletions(-)
tools/perf/builtin-annotate.c +5 -0
@@ -345,6 +345,10 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)setup_browser (true); + annotate.tool.machines =machines__new (); +if (annotate.tool.machines ==NULL ) +return -1 ; + annotate.session =perf_session__new (&file, false, &annotate.tool);if (annotate.session ==NULL )return -1 ;
@@ -384,6 +388,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)* up we'll get here. * * perf_session__delete(session); +* machines__delete(annotate.tool.machines); */ return ret; }
tools/perf/builtin-buildid-cache.c +12 -2
@@ -317,6 +317,9 @@ int cmd_buildid_cache(int argc, const char **argv, .mode = PERF_DATA_MODE_READ, };struct perf_session *session =NULL ; +struct perf_tool tool = { + .machines =NULL , + };const struct option buildid_cache_options[] = { OPT_STRING('a', "add", &add_name_list_str,
@@ -352,9 +355,14 @@ int cmd_buildid_cache(int argc, const char **argv, file.path = missing_filename; file.force = force; - session =perf_session__new (&file, false,NULL ); -if (session ==NULL ) + ret =-1 ; + + tool.machines =machines__new (); +if (tool.machines ==NULL )return -1 ; + session =perf_session__new (&file, false, &tool); +if (session ==NULL ) + goto out; }if (symbol__init (session ? &session->header.env :NULL ) <0 )
@@ -443,6 +451,8 @@ int cmd_buildid_cache(int argc, const char **argv, out:if (session)perf_session__delete (session); +if (tool.machines) +machines__delete (tool.machines);return ret; }
tools/perf/builtin-buildid-list.c +13 -3
@@ -58,6 +58,8 @@ static int perf_session__list_build_ids(bool force, bool with_hits) .mode = PERF_DATA_MODE_READ, .force = force, }; +struct perf_tool *tool = &build_id__mark_dso_hit_ops; +int ret =-1 ;symbol__elf_init ();/*
@@ -66,10 +68,14 @@ static int perf_session__list_build_ids(bool force, bool with_hits)if (filename__fprintf_build_id (input_name, stdout)) goto out; - session =perf_session__new (&file, false, &build_id__mark_dso_hit_ops); -if (session ==NULL ) + tool->machines =machines__new (); +if (tool->machines ==NULL )return -1 ; + session =perf_session__new (&file, false, tool); +if (session ==NULL ) + goto out_delete; +/* * We take all buildids when the file contains AUX area tracing data * because we do not decode the trace because it would take too long.
@@ -86,9 +92,13 @@ static int perf_session__list_build_ids(bool force, bool with_hits)perf_session__process_events (session);perf_session__fprintf_dsos_buildid (session, stdout, dso__skip_buildid, with_hits); + ret =0 ; +perf_session__delete (session); +out_delete: +machines__delete (tool->machines); out: -return 0 ; +return ret; } int cmd_buildid_list(int argc, const char **argv,
tools/perf/builtin-diff.c +14 -2
@@ -743,10 +743,17 @@ static int __cmd_diff(void)int ret = -EINVAL , i;data__for_each_file (i, d) { + ret = -ENOMEM ; + + tool.machines =machines__new (); +if (tool.machines ==NULL ) { +pr_err ("Failed to allocate machines\n" ); + goto out_delete; + } + d->session =perf_session__new (&d->file, false, &tool);if (!d->session) {pr_err ("Failed to open %s\n" , d->file.path); - ret =-1 ; goto out_delete; }
@@ -763,8 +770,13 @@ static int __cmd_diff(void) out_delete:data__for_each_file (i, d) { -if (d->session) +if (d->session) { +struct machines *machines = &d->session->machines; +perf_session__delete (d->session); +if (machines) +machines__delete (machines); + }data__free (d); }
tools/perf/builtin-evlist.c +15 -3
@@ -26,16 +26,28 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details .mode = PERF_DATA_MODE_READ, .force = details->force, }; +struct perf_tool tool = { + .machines =NULL , + }; +int ret =-1 ; - session =perf_session__new (&file,0 ,NULL ); -if (session ==NULL ) + tool.machines =machines__new (); +if (tool.machines ==NULL )return -1 ; + session =perf_session__new (&file,0 , &tool); +if (session ==NULL ) + goto out; +evlist__for_each (session->evlist, pos)perf_evsel__fprintf (pos, details, stdout); + ret =0 ; +perf_session__delete (session); -return 0 ; +out: +machines__delete (tool.machines); +return ret; }int cmd_evlist (int argc,const char **argv,const char *prefix__maybe_unused )
tools/perf/builtin-inject.c +5 -0
@@ -625,6 +625,10 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) inject.tool.ordered_events = inject.sched_stat; + inject.tool.machines =machines__new (); +if (inject.tool.machines ==NULL ) +return -1 ; + file.path = inject.input_name; inject.session =perf_session__new (&file, true, &inject.tool);if (inject.session ==NULL )
@@ -636,6 +640,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) ret =__cmd_inject (&inject);perf_session__delete (inject.session); +machines__delete (inject.tool.machines);return ret; }
tools/perf/builtin-kmem.c +4 -0
@@ -1908,6 +1908,9 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) file.path = input_name; + perf_kmem.machines =machines__new (); +if (perf_kmem.machines ==NULL ) +return -1 ; kmem_session = session =perf_session__new (&file, false, &perf_kmem);if (session ==NULL )return -1 ;
@@ -1963,6 +1966,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) out_delete:perf_session__delete (session); +machines__delete (perf_kmem.machines);return ret; }
tools/perf/builtin-kvm.c +12 -0
@@ -1053,6 +1053,10 @@ static int read_events(struct perf_kvm_stat *kvm) }; kvm->tool = eops; + kvm->tool.machines =machines__new (); +if (kvm->tool.machines ==NULL ) +return -1 ; + kvm->session =perf_session__new (&file, false, &kvm->tool);if (!kvm->session) {pr_err ("Initializing perf session failed\n" );
@@ -1381,6 +1385,12 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,if (perf_evlist__create_maps (kvm->evlist, &kvm->opts.target) <0 )usage_with_options (live_usage, live_options); + kvm->tool.machines =machines__new (); +if (kvm->tool.machines ==NULL ) { + err =-1 ; + goto out; + } +/* * perf session */
@@ -1406,6 +1416,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,if (kvm->session)perf_session__delete (kvm->session); kvm->session =NULL ; +if (kvm->tool.machines) +machines__delete (kvm->tool.machines);if (kvm->evlist)perf_evlist__delete (kvm->evlist);
tools/perf/builtin-lock.c +6 -1
@@ -866,10 +866,13 @@ static int __cmd_report(bool display_info) .force = force, }; + eops.machines =machines__new (); +if (eops.machines ==NULL ) +return -1 ; session =perf_session__new (&file, false, &eops);if (!session) {pr_err ("Initializing perf session failed\n" ); -return -1 ; + goto out_delete_machine; }symbol__init (&session->header.env);
@@ -899,6 +902,8 @@ static int __cmd_report(bool display_info) out_delete:perf_session__delete (session); +out_delete_machine: +machines__delete (eops.machines);return err; }
tools/perf/builtin-mem.c +9 -3
@@ -126,12 +126,16 @@ static int report_raw_events(struct perf_mem *mem) };int err = -EINVAL ;int ret; -struct perf_session *session =perf_session__new (&file, false, - &mem->tool); +struct perf_session *session; -if (session ==NULL ) + mem->tool.machines =machines__new (); +if (mem->tool.machines ==NULL )return -1 ; + session =perf_session__new (&file, false, &mem->tool); +if (session ==NULL ) + goto out_delete_machine; +if (mem->cpu_list) { ret =perf_session__cpu_bitmap (session, mem->cpu_list, mem->cpu_bitmap);
@@ -152,6 +156,8 @@ static int report_raw_events(struct perf_mem *mem) out_delete:perf_session__delete (session); +out_delete_machine: +machines__delete (mem->tool.machines);return err; }
tools/perf/builtin-record.c +7 -1
@@ -667,10 +667,14 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)else signal (SIGUSR2 ,SIG_IGN ); + tool->machines =machines__new (); +if (tool->machines ==NULL ) +return -1 ; + session =perf_session__new (file, false, tool);if (session ==NULL ) {pr_err ("Perf session creation failed.\n" ); -return -1 ; + goto out_delete_machines; } fd =perf_data_file__fd (file);
@@ -929,6 +933,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) out_delete_session:perf_session__delete (session); +out_delete_machines: +machines__delete (tool->machines);return status; }
tools/perf/builtin-report.c +8 -1
@@ -766,9 +766,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) file.force = report.force; repeat: + report.tool.machines =machines__new (); +if (report.tool.machines ==NULL ) +return -1 ; + session =perf_session__new (&file, false, &report.tool);if (session ==NULL ) -return -1 ; + goto error_delete;if (report.queue_size) { ordered_events__set_alloc_size(&session->ordered_events,
@@ -883,11 +887,14 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ret =__cmd_report (&report);if (ret ==K_SWITCH_INPUT_DATA ) {perf_session__delete (session); +machines__delete (report.tool.machines); goto repeat; }else ret =0 ; error:perf_session__delete (session); +error_delete: +machines__delete (report.tool.machines);return ret; }
tools/perf/builtin-sched.c +7 -1
@@ -1531,10 +1531,14 @@ static int perf_sched__read_events(struct perf_sched *sched) };int rc =-1 ; + sched->tool.machines =machines__new (); +if (sched->tool.machines ==NULL ) +return -1 ; + session =perf_session__new (&file, false, &sched->tool);if (session ==NULL ) {pr_debug ("No Memory for session\n" ); -return -1 ; + goto out_delete_machine; }symbol__init (&session->header.env);
@@ -1557,6 +1561,8 @@ static int perf_sched__read_events(struct perf_sched *sched) rc =0 ; out_delete:perf_session__delete (session); +out_delete_machine: +machines__delete (sched->tool.machines);return rc; }
tools/perf/builtin-script.c +24 -8
@@ -1388,21 +1388,27 @@ int find_scripts(char **scripts_array, char **scripts_path_array) .path = input_name, .mode = PERF_DATA_MODE_READ, }; +struct perf_tool tool = { + .machines =NULL , + };char *temp; -int i =0 ; +int i =-1 ; - session =perf_session__new (&file, false,NULL ); -if (!session) + tool.machines =machines__new (); +if (tool.machines ==NULL )return -1 ; + session =perf_session__new (&file, false, &tool); +if (!session) + goto out; +snprintf (scripts_path,MAXPATHLEN ,"%s/scripts" ,perf_exec_path ()); scripts_dir =opendir (scripts_path); -if (!scripts_dir) { -perf_session__delete (session); -return -1 ; - } +if (!scripts_dir) + goto out_session; + i =0 ;for_each_lang (scripts_path, scripts_dir, lang_dirent, lang_next) {snprintf (lang_path,MAXPATHLEN ,"%s/%s" , scripts_path, lang_dirent.d_name);
@@ -1440,7 +1446,11 @@ int find_scripts(char **scripts_array, char **scripts_path_array) }closedir (scripts_dir); + +out_session:perf_session__delete (session); +out: +machines__delete (tool.machines);return i; }
@@ -1802,9 +1812,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)if (!script_name)setup_pager (); + script.tool.machines =machines__new (); +if (script.tool.machines ==NULL ) +return -1 ; + session =perf_session__new (&file, false, &script.tool);if (session ==NULL ) -return -1 ; + goto out_delete_machine;if (header || header_only) {perf_session__fprintf_info (session, stdout, show_full_info);
@@ -1890,6 +1904,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) out_delete:perf_session__delete (session); +out_delete_machine: +machines__delete (script.tool.machines);if (script_started)cleanup_scripting ();
tools/perf/builtin-timechart.c +9 -3
@@ -1603,13 +1603,17 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) .force = tchart->force, }; -struct perf_session *session =perf_session__new (&file, false, - &tchart->tool); +struct perf_session *session;int ret = -EINVAL ; -if (session ==NULL ) + tchart->tool.machines =machines__new (); +if (tchart->tool.machines ==NULL )return -1 ; + session =perf_session__new (&file, false, &tchart->tool); +if (session ==NULL ) + goto out_delete_machine; +symbol__init (&session->header.env); (void)perf_header__process_sections(&session->header,
@@ -1640,6 +1644,8 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) (tchart->last_time - tchart->first_time) / 1000000000.0, output_name); out_delete:perf_session__delete (session); +out_delete_machine: +machines__delete (tchart->tool.machines);return ret; }
tools/perf/builtin-top.c +12 -3
@@ -933,12 +933,19 @@ static int __cmd_top(struct perf_top *top) {struct record_opts *opts = &top->record_opts;pthread_t thread; -int ret; +int ret =-1 ; +struct perf_tool tool = { + .machines =NULL , + }; - top->session =perf_session__new (NULL , false,NULL ); -if (top->session ==NULL ) + tool.machines =machines__new (); +if (tool.machines ==NULL )return -1 ; + top->session =perf_session__new (NULL , false, &tool); +if (top->session ==NULL ) + goto out_delete_machine; +machines__set_symbol_filter (&top->session->machines, symbol_filter);if (!objdump_path) {
@@ -1008,6 +1015,8 @@ static int __cmd_top(struct perf_top *top) out_delete:perf_session__delete (top->session); top->session =NULL ; +out_delete_machine: +machines__delete (tool.machines);return ret; }
tools/perf/builtin-trace.c +7 -1
@@ -2413,9 +2413,13 @@ static int trace__replay(struct trace *trace)/* add tid to output */ trace->multiple_threads = true; + trace->tool.machines =machines__new (); +if (trace->tool.machines ==NULL ) +return -1 ; + session =perf_session__new (&file, false, &trace->tool);if (session ==NULL ) -return -1 ; + goto out_delete;if (symbol__init (&session->header.env) <0 ) goto out;
@@ -2475,6 +2479,8 @@ static int trace__replay(struct trace *trace) out:perf_session__delete (session); +out_delete: +machines__delete (trace->tool.machines);return err; }
tools/perf/util/data-convert-bt.c +7 -1
@@ -1133,10 +1133,14 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force)if (ctf_writer__init (cw, path))return -1 ; + c.tool.machines =machines__new (); +if (c.tool.machines ==NULL ) + goto free_writer; +/* perf.data session */ session =perf_session__new (&file,0 , &c.tool);if (!session) - goto free_writer; + goto free_machines;if (c.queue_size) { ordered_events__set_alloc_size(&session->ordered_events,
@@ -1176,6 +1180,8 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force) free_session:perf_session__delete (session); +free_machines: +machines__delete (c.tool.machines); free_writer:ctf_writer__cleanup (cw);pr_err ("Error during conversion setup.\n" );
tools/perf/util/machine.c +15 -0
@@ -163,6 +163,21 @@ void machines__exit(struct machines *machines)/* XXX exit guest */ } +struct machines *machines__new (void ) +{ +struct machines *machines =zalloc (sizeof (*machines)); + +if (machines) +machines__init (machines); +return machines; +} + +void machines__delete (struct machines *machines) +{ +machines__exit (machines); +free (machines); +} +struct machine *machines__add (struct machines *machines,pid_t pid,const char *root_dir) {
tools/perf/util/machine.h +3 -0
@@ -102,6 +102,9 @@ struct machines {void machines__init (struct machines *machines);void machines__exit (struct machines *machines); +struct machines *machines__new (void ); +void machines__delete (struct machines *machines); +void machines__process_guests (struct machines *machines,machine__process_t process,void *data);
tools/perf/util/tool.h +2 -0
@@ -12,6 +12,7 @@ struct perf_evsel;struct perf_sample ;struct perf_tool ;struct machine ; +struct machines ;struct ordered_events ; typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event,
@@ -35,6 +36,7 @@ typedef s64 (*event_op3)(struct perf_tool *tool, union perf_event *event, struct perf_session *session);struct perf_tool { +struct machines *machines;event_sample sample, read; event_op mmap,
Signature · 1 line
2.4.1
Namhyung Kim
maintainerPATCH 2/2 perf session: Separate struct machines from session
In reply to Namhyung KimWith multi-thread report, separate sessions can be passed to each thread, in this case we should keep a single machine state for all struct sessions. Separate machines and have a pointer in sessions.
--- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-diff.c | 2 +- tools/perf/builtin-kmem.c | 10 +++++----- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-record.c | 6 +++--- tools/perf/builtin-report.c | 4 ++-- tools/perf/builtin-top.c | 10 +++++----- tools/perf/builtin-trace.c | 2 +- tools/perf/util/build-id.c | 16 ++++++++-------- tools/perf/util/intel-bts.c | 2 +- tools/perf/util/intel-pt.c | 2 +- tools/perf/util/session.c | 30 +++++++++++++++--------------- tools/perf/util/session.h | 6 +++--- 13 files changed, 47 insertions(+), 47 deletions(-)
tools/perf/builtin-annotate.c +1 -1
@@ -196,7 +196,7 @@ static int __cmd_annotate(struct perf_annotate *ann)struct perf_evsel *pos;u64 total_nr_samples; -machines__set_symbol_filter (&session->machines, symbol__annotate_init); +machines__set_symbol_filter (session->machines, symbol__annotate_init);if (ann->cpu_list) { ret = perf_session__cpu_bitmap(session, ann->cpu_list,
tools/perf/builtin-diff.c +1 -1
@@ -771,7 +771,7 @@ static int __cmd_diff(void) out_delete:data__for_each_file (i, d) {if (d->session) { -struct machines *machines = &d->session->machines; +struct machines *machines = d->session->machines;perf_session__delete (d->session);if (machines)
tools/perf/builtin-kmem.c +5 -5
@@ -316,7 +316,7 @@ static int build_alloc_func_list(void)struct symbol *sym;struct rb_node *node;struct alloc_func *func; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;regex_t alloc_func_regex;const char pattern[] ="^_?_?(alloc|get_free|get_zeroed)_pages?" ;
@@ -366,7 +366,7 @@ static int build_alloc_func_list(void)static u64 find_callsite (struct perf_evsel *evsel,struct perf_sample *sample) {struct addr_location al; -struct machine *machine = &kmem_session->machines.host; +struct machine *machine = &kmem_session->machines->host;struct callchain_cursor_node *node;if (alloc_func_list ==NULL ) {
@@ -949,7 +949,7 @@ static void __print_slab_result(struct rb_root *root, int n_lines, int is_caller) {struct rb_node *next; -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;printf ("%.105s\n" , graph_dotted_line);printf (" %-34s |" , is_caller ?"Callsite" :"Alloc Ptr" );
@@ -1010,7 +1010,7 @@ static const char * const migrate_type_str[] = {static void __print_page_alloc_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_alloc_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;const char *format;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);
@@ -1060,7 +1060,7 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)static void __print_page_caller_result (struct perf_session *session,int n_lines) {struct rb_node *next =rb_first (&page_caller_sorted); -struct machine *machine = &session->machines.host; +struct machine *machine = &session->machines->host;int gfp_len =max (strlen ("GFP flags" ), max_gfp_len);printf ("\n%.105s\n" , graph_dotted_line);
tools/perf/builtin-kvm.c +1 -1
@@ -1402,7 +1402,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, kvm->session->evlist = kvm->evlist;perf_session__set_id_hdr_size (kvm->session);ordered_events__set_copy_on_queue (&kvm->session->ordered_events, true); -machine__synthesize_threads (&kvm->session->machines.host, &kvm->opts.target, +machine__synthesize_threads (&kvm->session->machines->host, &kvm->opts.target, kvm->evlist->threads, false); err =kvm_live_open_events (kvm);if (err)
tools/perf/builtin-record.c +3 -3
@@ -607,7 +607,7 @@ static int synthesize_workload_comm_event(struct perf_evlist *evlist, void *arg) {union perf_event *event;struct record *rec = arg; -struct machine *machine = &rec->session->machines.host; +struct machine *machine = &rec->session->machines->host;int pid = evlist->workload.pid;const char *comm_str = program_invocation_short_name;size_t comm_size, total_size;
@@ -724,7 +724,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } - machine = &session->machines.host; + machine = &session->machines->host;if (file->is_pipe) { err = perf_event__synthesize_attrs(tool, session,
@@ -775,7 +775,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) "Check /proc/modules permission or run as root.\n");if (perf_guest) { -machines__process_guests (&session->machines, +machines__process_guests (session->machines, perf_event__synthesize_guest_os, tool); }
tools/perf/builtin-report.c +2 -2
@@ -353,7 +353,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,static void report__warn_kptr_restrict (const struct report *rep) { -struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION ]; +struct map *kernel_map = rep->session->machines->host.vmlinux_maps[MAP__FUNCTION ];struct kmap *kernel_kmap = kernel_map ?map__kmap (kernel_map) :NULL ; if (kernel_map == NULL ||
@@ -849,7 +849,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)*/ if (ui__has_annotation ()) { symbol_conf.priv_size =sizeof (struct annotation ); -machines__set_symbol_filter (&session->machines, +machines__set_symbol_filter (session->machines, symbol__annotate_init);/* * For searching by name on the "Browse map details".
tools/perf/builtin-top.c +5 -5
@@ -828,13 +828,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) ++top->us_samples;if (top->hide_user_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_KERNEL : ++top->kernel_samples;if (top->hide_kernel_symbols) goto next_event; - machine = &session->machines.host; + machine = &session->machines->host;break ;case PERF_RECORD_MISC_GUEST_KERNEL : ++top->guest_kernel_samples;
@@ -942,12 +942,12 @@ static int __cmd_top(struct perf_top *top)if (tool.machines ==NULL )return -1 ; +machines__set_symbol_filter (tool.machines, symbol_filter); + top->session =perf_session__new (NULL , false, &tool);if (top->session ==NULL ) goto out_delete_machine; -machines__set_symbol_filter (&top->session->machines, symbol_filter); -if (!objdump_path) { ret =perf_session_env__lookup_objdump (&top->session->header.env);if (ret)
@@ -958,7 +958,7 @@ static int __cmd_top(struct perf_top *top)if (ret) goto out_delete; -machine__synthesize_threads (&top->session->machines.host, &opts->target, +machine__synthesize_threads (&top->session->machines->host, &opts->target, top->evlist->threads, false); ret =perf_top__start_counters (top);if (ret)
tools/perf/builtin-trace.c +1 -1
@@ -2424,7 +2424,7 @@ static int trace__replay(struct trace *trace)if (symbol__init (&session->header.env) <0 ) goto out; - trace->host = &session->machines.host; + trace->host = &session->machines->host; err =perf_session__set_tracepoints_handlers (session, handlers);if (err)
tools/perf/util/build-id.c +8 -8
@@ -221,12 +221,12 @@ static int machine__write_buildid_table(struct machine *machine, int fd)int perf_session__write_buildid_table (struct perf_session *session,int fd) {struct rb_node *nd; -int err =machine__write_buildid_table (&session->machines.host, fd); +int err =machine__write_buildid_table (&session->machines->host, fd);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__write_buildid_table (pos, fd);if (err)
@@ -261,11 +261,11 @@ int dsos__hit_all(struct perf_session *session)struct rb_node *nd;int err; - err =machine__hit_all_dsos (&session->machines.host); + err =machine__hit_all_dsos (&session->machines->host);if (err)return err; -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); err =machine__hit_all_dsos (pos);
@@ -509,9 +509,9 @@ int perf_session__cache_build_ids(struct perf_session *session)if (mkdir (buildid_dir,0755 ) !=0 && errno !=EEXIST )return -1 ; - ret =machine__cache_build_ids (&session->machines.host); + ret =machine__cache_build_ids (&session->machines->host); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__cache_build_ids (pos); }
@@ -530,9 +530,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits)bool perf_session__read_build_ids (struct perf_session *session,bool with_hits) {struct rb_node *nd; -bool ret =machine__read_build_ids (&session->machines.host, with_hits); +bool ret =machine__read_build_ids (&session->machines->host, with_hits); -for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { +for (nd =rb_first (&session->machines->guests); nd; nd =rb_next (nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret |=machine__read_build_ids (pos, with_hits); }
tools/perf/util/intel-bts.c +1 -1
@@ -862,7 +862,7 @@ int intel_bts_process_auxtrace_info(union perf_event *event, goto err_free; bts->session = session; - bts->machine = &session->machines.host;/* No kvm support */ + bts->machine = &session->machines->host;/* No kvm support */ bts->auxtrace_type = auxtrace_info->type; bts->pmu_type = auxtrace_info->priv[INTEL_BTS_PMU_TYPE ]; bts->tc.time_shift = auxtrace_info->priv[INTEL_BTS_TIME_SHIFT ];
tools/perf/util/intel-pt.c +1 -1
@@ -1788,7 +1788,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,intel_pt_log_set_name (INTEL_PT_PMU_NAME ); pt->session = session; - pt->machine = &session->machines.host;/* No kvm support */ + pt->machine = &session->machines->host;/* No kvm support */ pt->auxtrace_type = auxtrace_info->type; pt->pmu_type = auxtrace_info->priv[INTEL_PT_PMU_TYPE ]; pt->tc.time_shift = auxtrace_info->priv[INTEL_PT_TIME_SHIFT ];
tools/perf/util/session.c +15 -15
@@ -58,21 +58,21 @@ void perf_session__set_id_hdr_size(struct perf_session *session) {u16 id_hdr_size =perf_evlist__id_hdr_size (session->evlist); -machines__set_id_hdr_size (&session->machines, id_hdr_size); +machines__set_id_hdr_size (session->machines, id_hdr_size); }int perf_session__create_kernel_maps (struct perf_session *session) { -int ret =machine__create_kernel_maps (&session->machines.host); +int ret =machine__create_kernel_maps (&session->machines->host);if (ret >=0 ) - ret =machines__create_guest_kernel_maps (&session->machines); + ret =machines__create_guest_kernel_maps (session->machines);return ret; }static void perf_session__destroy_kernel_maps (struct perf_session *session) { -machines__destroy_kernel_maps (&session->machines); +machines__destroy_kernel_maps (session->machines); }static bool perf_session__has_comm_exec (struct perf_session *session)
@@ -91,7 +91,7 @@ static void perf_session__set_comm_exec(struct perf_session *session) {bool comm_exec =perf_session__has_comm_exec (session); -machines__set_comm_exec (&session->machines, comm_exec); +machines__set_comm_exec (session->machines, comm_exec); } static int ordered_events__deliver_event(struct ordered_events *oe,
@@ -123,7 +123,8 @@ struct perf_session *perf_session__new(struct perf_data_file *file, session->repipe = repipe; session->tool = tool;INIT_LIST_HEAD (&session->auxtrace_index); -machines__init (&session->machines); + + session->machines = tool->machines;ordered_events__init (&session->ordered_events, ordered_events__deliver_event);if (file) {
@@ -168,7 +169,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,static void perf_session__delete_threads (struct perf_session *session) { -machine__delete_threads (&session->machines.host); +machine__delete_threads (&session->machines->host); }static void perf_session_env__delete (struct perf_session_env *env)
@@ -194,7 +195,6 @@ void perf_session__delete(struct perf_session *session)perf_session__destroy_kernel_maps (session);perf_session__delete_threads (session);perf_session_env__delete (&session->header.env); -machines__exit (&session->machines);if (session->file)perf_data_file__close (session->file);free (session->header.index);
@@ -1088,7 +1088,7 @@ static int perf_session__deliver_event(struct perf_session *session,if (ret >0 )return 0 ; -return machines__deliver_event (&session->machines, stats, +return machines__deliver_event (session->machines, stats, session->evlist, event, sample, tool, file_offset); }
@@ -1155,7 +1155,7 @@ int perf_session__deliver_synth_event(struct perf_session *session,if (event->header.type >=PERF_RECORD_USER_TYPE_START )return perf_session__process_user_event (session, event,0 ); -return machines__deliver_event (&session->machines, &evlist->stats, +return machines__deliver_event (session->machines, &evlist->stats, evlist, event, sample, tool,0 ); }
@@ -1270,14 +1270,14 @@ void perf_event_header__bswap(struct perf_event_header *hdr)struct thread *perf_session__findnew (struct perf_session *session,pid_t pid) { -return machine__findnew_thread (&session->machines.host,-1 , pid); +return machine__findnew_thread (&session->machines->host,-1 , pid); }static struct thread *perf_session__register_idle_thread (struct perf_session *session) {struct thread *thread; - thread =machine__findnew_thread (&session->machines.host,0 ,0 ); + thread =machine__findnew_thread (&session->machines->host,0 ,0 );if (thread ==NULL ||thread__set_comm (thread,"swapper" ,0 )) {pr_err ("problem inserting idle task.\n" ); thread =NULL ;
@@ -1692,13 +1692,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps,size_t perf_session__fprintf_dsos (struct perf_session *session,FILE *fp) { -return machines__fprintf_dsos (&session->machines, fp); +return machines__fprintf_dsos (session->machines, fp); }size_t perf_session__fprintf_dsos_buildid (struct perf_session *session,FILE *fp,bool (skip )(struct dso *dso,int parm),int parm) { -return machines__fprintf_dsos_buildid (&session->machines, fp, skip, parm); +return machines__fprintf_dsos_buildid (session->machines, fp, skip, parm); }size_t perf_session__fprintf_nr_events (struct perf_session *session,FILE *fp)
@@ -1721,7 +1721,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)* FIXME: Here we have to actually print all the machines in this * session, not just the host... */ -return machine__fprintf (&session->machines.host, fp); +return machine__fprintf (&session->machines->host, fp); } struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
tools/perf/util/session.h +3 -3
@@ -20,7 +20,7 @@ struct itrace_synth_opts;struct perf_session {struct perf_header header; -struct machines machines; +struct machines *machines;struct perf_evlist *evlist;struct auxtrace *auxtrace;struct itrace_synth_opts *itrace_synth_opts;
@@ -79,13 +79,13 @@ void perf_session__set_id_hdr_size(struct perf_session *session);static inline struct machine *perf_session__find_machine (struct perf_session *session,pid_t pid) { -return machines__find (&session->machines, pid); +return machines__find (session->machines, pid); }static inline struct machine *perf_session__findnew_machine (struct perf_session *session,pid_t pid) { -return machines__findnew (&session->machines, pid); +return machines__findnew (session->machines, pid); }struct thread *perf_session__findnew (struct perf_session *session,pid_t pid);
Signature · 1 line
2.4.1
Namhyung Kim
maintainerPATCH 34/40 perf report: Parallelize perf report using multi-thread
In reply to Namhyung KimIntroduce perf_session__process_events_mt() to enable multi-thread sample processing. It allocates a struct perf_tool_mt and fills needed info in it.
The session and hists event stats are counted for each thread and summed after finishing the processing. Similarly hist entries are added to per-thread hists first and then move to the original hists using hists__mt_resort(). This function reuses hists__collapse_ resort() code so makes sort__need_collapse force to true and skips the collapsing function.
Note that most of preprocessing stage is already done by processing meta events in dummy tracking evsel first. We can find corresponding thread and map based on the sample time and symbol loading and dso cache access is protected by pthread mutex.
--- tools/perf/util/hist.c | 75 +++++++++++++++++++---- tools/perf/util/hist.h | 3 + tools/perf/util/session.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/session.h | 1 + tools/perf/util/tool.h | 12 ++++ 5 files changed, 231 insertions(+), 13 deletions(-)
tools/perf/util/hist.c +62 -13
@@ -940,7 +940,7 @@ void hist_entry__delete(struct hist_entry *he)* collapse the histogram */ -static bool hists__collapse_insert_entry (struct hists *hists__maybe_unused , +static bool hists__collapse_insert_entry (struct hists *hists,struct rb_root *root,struct hist_entry *he) {
@@ -977,6 +977,13 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, } hists->nr_entries++; +/* +* For multi-threaded report, he->hists points to a dummy +* hists in the struct perf_tool_mt. Please see +* perf_session__process_events_mt(). +*/ + he->hists = hists; +rb_link_node (&he->rb_node_in, parent, p);rb_insert_color (&he->rb_node_in, root);return true;
@@ -1004,19 +1011,12 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)hists__filter_entry_by_symbol (hists, he); } -void hists__collapse_resort (struct hists *hists,struct ui_progress *prog) +static void __hists__collapse_resort (struct hists *hists,struct rb_root *root, +struct ui_progress *prog) { -struct rb_root *root;struct rb_node *next;struct hist_entry *n; -if (!sort__need_collapse) -return ; - - hists->nr_entries =0 ; - - root =hists__get_rotate_entries_in (hists); - next =rb_first (root);while (next) {
@@ -1039,6 +1039,27 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) } } +void hists__collapse_resort (struct hists *hists,struct ui_progress *prog) +{ +struct rb_root *root; + +if (!sort__need_collapse) +return ; + + hists->nr_entries =0 ; + + root =hists__get_rotate_entries_in (hists); +__hists__collapse_resort (hists, root, prog); +} + +void hists__mt_resort (struct hists *dst,struct hists *src) +{ +struct rb_root *root = src->entries_in; + + sort__need_collapse =1 ; +__hists__collapse_resort (dst, root,NULL ); +} +static int hist_entry__sort (struct hist_entry *a,struct hist_entry *b) {struct perf_hpp_fmt *fmt;
@@ -1268,6 +1289,29 @@ void events_stats__inc(struct events_stats *stats, u32 type) ++stats->nr_events[type]; } +void events_stats__add (struct events_stats *dst,struct events_stats *src) +{ +int i; + +#define ADD (_field) dst->_field += src->_field + +ADD (total_period); +ADD (total_non_filtered_period); +ADD (total_lost); +ADD (total_invalid_chains); +ADD (nr_non_filtered_samples); +ADD (nr_lost_warned); +ADD (nr_unknown_events); +ADD (nr_invalid_chains); +ADD (nr_unknown_id); +ADD (nr_unprocessable_samples); + +for (i =0 ; i <PERF_RECORD_HEADER_MAX ; i++) +ADD (nr_events[i]); + +#undef ADD +} +void hists__inc_nr_events (struct hists *hists,u32 type) {events_stats__inc (&hists->stats, type);
@@ -1444,16 +1488,21 @@ int perf_hist_config(const char *var, const char *value)return 0 ; } -static int hists_evsel__init (struct perf_evsel *evsel) +void __hists__init (struct hists *hists) { -struct hists *hists =evsel__hists (evsel); -memset (hists,0 ,sizeof (*hists)); hists->entries_in_array[0 ] = hists->entries_in_array[1 ] =RB_ROOT ; hists->entries_in = &hists->entries_in_array[0 ]; hists->entries_collapsed =RB_ROOT ; hists->entries =RB_ROOT ;pthread_mutex_init (&hists->lock,NULL ); +} + +static int hists_evsel__init (struct perf_evsel *evsel) +{ +struct hists *hists =evsel__hists (evsel); + +__hists__init (hists);return 0 ; }
tools/perf/util/hist.h +3 -0
@@ -122,6 +122,7 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,void hist_entry__delete (struct hist_entry *he);void hists__output_resort (struct hists *hists,struct ui_progress *prog); +void hists__mt_resort (struct hists *dst,struct hists *src);void hists__collapse_resort (struct hists *hists,struct ui_progress *prog);void hists__decay_entries (struct hists *hists,bool zap_user,bool zap_kernel);
@@ -134,6 +135,7 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h);void hists__inc_nr_events (struct hists *hists,u32 type);void hists__inc_nr_samples (struct hists *hists,bool filtered);void events_stats__inc (struct events_stats *stats,u32 type); +void events_stats__add (struct events_stats *dst,struct events_stats *src);size_t events_stats__fprintf (struct events_stats *stats,FILE *fp); size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
@@ -177,6 +179,7 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel) }int hists__init (void ); +void __hists__init (struct hists *hists);struct perf_hpp {char *buf;
tools/perf/util/session.c +153 -0
159 lines not on this page · raw message
tools/perf/util/session.h +1 -0
@@ -56,6 +56,7 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, struct perf_sample *sample);int perf_session__process_events (struct perf_session *session); +int perf_session__process_events_mt (struct perf_session *session,void *arg);int perf_session__queue_event (struct perf_session *s,union perf_event *event,struct perf_sample *sample,u64 file_offset);
tools/perf/util/tool.h +12 -0
@@ -2,6 +2,7 @@#define __PERF_TOOL_H#include <stdbool.h> +#include "util/event.h" #include <linux/types.h>
@@ -13,6 +14,7 @@ struct perf_sample;struct perf_tool ;struct machine ;struct ordered_events ; +struct hists ; typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample,
@@ -59,4 +61,14 @@ struct perf_tool { bool ordering_requires_timestamps; }; +struct perf_tool_mt { +struct perf_tool tool; +struct events_stats stats; +struct hists *hists; +struct perf_session *session; +int idx; + +void *priv; +}; +#endif /* __PERF_TOOL_H */
Signature · 1 line
2.4.0
Re: [PATCH 34/40] perf report: Parallelize perf report using multi-thread
On Mon, May 18, 2015 at 09:30:49AM +0900, Namhyung Kim wrote:
SNIP
Quoted 17 lines
tools/perf/util/session.c +17
-0+ +int perf_session__process_events_mt (struct perf_session *session,void *arg) +{ +struct perf_data_file *file = session->file; +struct perf_evlist *evlist = session->evlist; +struct perf_evsel *evsel; +u64 nr_entries =0 ; +struct perf_tool *tool = session->tool; +struct perf_session *mt_sessions =NULL ; +struct perf_tool_mt *mt_tools =NULL ; +struct perf_session *ms; +struct perf_tool_mt *mt; +pthread_t *th_id; +int err, i, k; +int nr_index = session->header.nr_index; +u64 size =perf_data_file__size (file); +
SNIP
Quoted 10 lines
tools/perf/util/session.c +10
-0+ +for (i =1 ; i < nr_index; i++) { + mt = &mt_tools[i]; + +evlist__for_each (evlist, evsel) { +struct hists *hists =evsel__hists (evsel); + +if (perf_evsel__is_dummy_tracking (evsel)) +continue ; +
tools/perf/util/session.c int perf_session__process_events(struct perf_session *session) · line 1778
Quoted 10 lines
tools/perf/util/session.c +10
-0+hists__mt_resort (hists, &mt->hists[evsel->idx]); + +/* Non-group events are considered as leader */ +if (symbol_conf.event_group && + !perf_evsel__is_group_leader (evsel)) { +struct hists *leader_hists; + + leader_hists =evsel__hists (evsel->leader); +hists__match (leader_hists, hists); +hists__link (leader_hists, hists);
hum, could you please describe/comment on why is this needed?
thanks,
jirka
Namhyung Kim
maintainerRe: [PATCH 34/40] perf report: Parallelize perf report using multi-thread
Quoted 38 lines · Jiri Olsa
On Mon, May 18, 2015 at 09:30:49AM +0900, Namhyung Kim wrote:
SNIP
Quoted 17 lines
tools/perf/util/session.c +17
-0+ +int perf_session__process_events_mt (struct perf_session *session,void *arg) +{ +struct perf_data_file *file = session->file; +struct perf_evlist *evlist = session->evlist; +struct perf_evsel *evsel; +u64 nr_entries =0 ; +struct perf_tool *tool = session->tool; +struct perf_session *mt_sessions =NULL ; +struct perf_tool_mt *mt_tools =NULL ; +struct perf_session *ms; +struct perf_tool_mt *mt; +pthread_t *th_id; +int err, i, k; +int nr_index = session->header.nr_index; +u64 size =perf_data_file__size (file); +SNIP
Quoted 14 lines
tools/perf/util/session.c +14
-0+ +for (i =1 ; i < nr_index; i++) { + mt = &mt_tools[i]; + +evlist__for_each (evlist, evsel) { +struct hists *hists =evsel__hists (evsel); + +if (perf_evsel__is_dummy_tracking (evsel)) +continue ; + +hists__mt_resort (hists, &mt->hists[evsel->idx]); + +/* Non-group events are considered as leader */ + if (symbol_conf.event_group &&
Quoted 8 lines
tools/perf/util/session.c int perf_session__process_events(struct perf_session *session) · line 1778
Quoted 6 lines
tools/perf/util/session.c +6
-0+ !perf_evsel__is_group_leader(evsel)) { +struct hists *leader_hists; + + leader_hists =evsel__hists (evsel->leader); +hists__match (leader_hists, hists); +hists__link (leader_hists, hists);hum, could you please describe/comment on why is this needed?
This is because it skips report__collapse_hists() when multi-thread processing is enabled. For multi-thread, it needs to collect per-thread hists into a global hists. This step is very similar to hists__collapse_resort() and called regardless of sort keys, so it skips to call the report__collapse_hists().
But I think it should be called after collapsing all per-thread hists. I'll move it out of the loop.
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 35/40 perf record: Synthesize COMM event for a command line workload
In reply to Namhyung KimWhen perf creates a new child to profile, the events are enabled on exec(). And in this case, it doesn't synthesize any event for the child since they'll be generated during exec(). But there's an window between the enabling and the event generation.
It used to be overcome since samples are only in kernel (so we always have the map) and the comm is overridden by a later COMM event. However it won't work anymore since those samples will go to a missing thread now but the COMM event will create a (current) thread. This leads to those early samples (like native_write_msr_safe) not having a comm but pid (like ':15328').
So it needs to synthesize COMM event for the child explicitly before enabling so that it can have a correct comm. But at this time, the comm will be "perf" since it's not exec-ed yet.
--- tools/perf/builtin-record.c | 18 +++++++++++++++++- tools/perf/util/event.c | 2 +- tools/perf/util/event.h | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-)
tools/perf/builtin-record.c +17 -1
@@ -766,8 +766,24 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)/* * Let the child rip */ -if (forks) +if (forks) { +union perf_event *comm_event; + + comm_event =malloc (sizeof (*comm_event) + machine->id_hdr_size); +if (comm_event ==NULL ) + goto out_child; + + err =perf_event__synthesize_comm (tool, comm_event, + rec->evlist->workload.pid, + process_synthesized_event, + machine); +free (comm_event); + +if (err <0 ) + goto out_child; +perf_evlist__start_workload (rec->evlist); + }if (opts->initial_delay) {usleep (opts->initial_delay *1000 );
tools/perf/util/event.c +1 -1
@@ -165,7 +165,7 @@ static int perf_event__prepare_comm(union perf_event *event, pid_t pid,return 0 ; } -static pid_t perf_event__synthesize_comm (struct perf_tool *tool, +pid_t perf_event__synthesize_comm (struct perf_tool *tool,union perf_event *event,pid_t pid,perf_event__handler_t process,struct machine *machine)
tools/perf/util/event.h +5 -0
@@ -452,6 +452,11 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,struct machine *machine, bool mmap_data); +pid_t perf_event__synthesize_comm (struct perf_tool *tool, +union perf_event *event,pid_t pid, +perf_event__handler_t process, +struct machine *machine); +size_t perf_event__fprintf_comm (union perf_event *event,FILE *fp);size_t perf_event__fprintf_mmap (union perf_event *event,FILE *fp);size_t perf_event__fprintf_mmap2 (union perf_event *event,FILE *fp);
Signature · 1 line
2.4.0
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 35/40] perf record: Synthesize COMM event for a command line workload
Em Mon, May 18, 2015 at 09:30:50AM +0900, Namhyung Kim escreveu:
Quoted 12 lines
When perf creates a new child to profile, the events are enabled on
exec(). And in this case, it doesn't synthesize any event for the child since they'll be generated duringexec(). But there's an window between the enabling and the event generation.It used to be overcome since samples are only in kernel (so we always have the map) and the comm is overridden by a later COMM event. However it won't work anymore since those samples will go to a missing thread now but the COMM event will create a (current) thread. This leads to those early samples (like
native_write_msr_safe) not having a comm but pid (like ':15328').
Quoted 3 lines
So it needs to synthesize COMM event for the child explicitly before enabling so that it can have a correct comm. But at this time, the comm will be "perf" since it's not exec-ed yet.
This looks reasonable, but I think it probably needs to be done somewhere in perf_evlist__prepare_workload() or perf_evlist__start_workload(), as this affects other tools as well, like 'top', 'trace' and any other that may want to do this start-workload use case.
I also wonder if we can't overcome this without using /proc, i.e. actually moving the "start the workload" to just before the fork, so that the kernel covers that as well.
Or, alternatively, the thread can be created without having to look at /proc at all, but by directly creating the struct thread, with the correct COMM, pid, etc, that we know, since we forked it, etc.
- Arnaldo
Quoted 68 lines
Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/builtin-record.c | 18 +++++++++++++++++- tools/perf/util/event.c | 2 +- tools/perf/util/event.h | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-)tools/perf/builtin-record.c +17
-1@@ -766,8 +766,24 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)/* * Let the child rip */ -if (forks) +if (forks) { +union perf_event *comm_event; + + comm_event =malloc (sizeof (*comm_event) + machine->id_hdr_size); +if (comm_event ==NULL ) + goto out_child; + + err =perf_event__synthesize_comm (tool, comm_event, + rec->evlist->workload.pid, + process_synthesized_event, + machine); +free (comm_event); + +if (err <0 ) + goto out_child; +perf_evlist__start_workload (rec->evlist); + }if (opts->initial_delay) {usleep (opts->initial_delay *1000 );tools/perf/util/event.c +1
-1@@ -165,7 +165,7 @@ static int perf_event__prepare_comm(union perf_event *event, pid_t pid,return 0 ; } -static pid_t perf_event__synthesize_comm (struct perf_tool *tool, +pid_t perf_event__synthesize_comm (struct perf_tool *tool,union perf_event *event,pid_t pid,perf_event__handler_t process,struct machine *machine)tools/perf/util/event.h +5
-0@@ -452,6 +452,11 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,struct machine *machine, bool mmap_data); +pid_t perf_event__synthesize_comm (struct perf_tool *tool, +union perf_event *event,pid_t pid, +perf_event__handler_t process, +struct machine *machine); +size_t perf_event__fprintf_comm (union perf_event *event,FILE *fp);size_t perf_event__fprintf_mmap (union perf_event *event,FILE *fp);size_t perf_event__fprintf_mmap2 (union perf_event *event,FILE *fp);Signature · 1 line
2.4.0