perf tools: Add threads to record command
Jiri Olsa
reviewercover perf tools: Add threads to record command
hi,
sending *RFC* for threads support in perf record command.
In big picture this patchset adds perf record --threads option that allows to create threads in following modes:
1) single thread mode (current)
$ perf record ... $ perf record --threads=1 ...
2) mode with specific (X) number of threads
$ perf record --threads=X ...
3) mode that creates thread for every monitored memory map .. which in perf record is equal to number of CPUs, and it pins each thread to its map's cpu
$ perf record --threads=X ...
The perf.data stays as a single file.
This patchset contains lot of preparation changes to make threaded record possible:
- Namhyung's changes to create multiple data streams in perf data file, which allows having each thread data being stored in separate files and merged into single perf data after
- Namhyung's changes to create track mmaps for auxiliary events
- Namhyung's changes to search for threads/mmaps/comms using the time. This is needed because we have now multiple data streams which are processed separately, but they all need access to complete auxiliary events data (threads/mmaps/comms). That's also a reason why the auxiliary events are stored into separate data stream, which is processed before real data.
- the rest of the code that adds threads abstraction into record command allows to create them and distribute maps among them
- other preparational changes
The threaded monitoring currently can't monitor backward maps and there are probably more limitations which I haven't spotted yet.
So far I tested on laptop:
http://people.redhat.com/~jolsa/record_threads/test-4CPU.txt
and a one bigger server:
http://people.redhat.com/~jolsa/record_threads/test-208CPU.txt
I can see decrease in recorded LOST events, but both the benchmark and the monitoring must be carefully configured wrt:
- number of events (frequency)
- size of the memory maps
- size of events (callchains)
- final perf.data size
It's also available in:
git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf.git
perf/record_threads
thoughts? ;-) thanks
jirka
---
Jiri Olsa (31):
perf tools: Remove perf_tool from event_op2
perf tools: Remove perf_tool from event_op3
perf tools: Pass struct perf_mmap into auxtrace_mmap__read* functions perf tools: Add struct perf_mmap arg into record__write
perf tools: Create separate mmap for dummy tracking event perf tools: Make copyfile_offset global
perf tools: Add perf_data__create_index function
perf record: Add --index option for building index table
perf tools: Convert dead thread list into rbtree
perf tools: Add thread::exited flag
perf callchain: Maintain libunwind's address space in map_groups perf tools: Rename perf_evlist__munmap_filtered to perf_mmap__put_filtered tools lib fd array: Introduce fdarray__add_clone function tools lib subcmd: Add OPT_INTEGER_OPTARG|_SET options
perf tools: Move __perf_session__process_events args into struct perf ui progress: Fix index progress display
perf tools: Add threads debug variable
perf tools: Add cpu into struct perf_mmap
perf tools: Add perf_mmap__read_tail function
perf record: Introduce struct record_thread
perf record: Read record thread's mmaps
perf record: Move waking into struct record
perf record: Move samples into struct record_thread
perf record: Move bytes_written into struct record_thread perf record: Add record_thread start/stop/process functions perf record: Wait for all threads being started
perf record: Add --threads option
perf record: Add --thread-stats option support
perf record: Add maps to --thread-stats output
perf record: Spread maps for --threads option
perf record: Spread maps for --threads=X option
Namhyung Kim (18):
perf tools: Use a software dummy event to track task/mmap events perf tools: Extend perf_evlist__mmap_ex() to use track mmap perf report: Skip dummy tracking event
perf tools: Add HEADER_DATA_INDEX feature
perf tools: Handle indexed data file properly
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: Introduce machine__find*_thread_by_time()
perf tools: Add a test case for timed thread handling
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
tools/lib/api/fd/array.c | 17 + tools/lib/api/fd/array.h | 1 + tools/lib/subcmd/parse-options.c | 2 + tools/lib/subcmd/parse-options.h | 9 + tools/perf/Documentation/perf-record.txt | 4 + tools/perf/Documentation/perf.txt | 1 + tools/perf/builtin-inject.c | 32 +- tools/perf/builtin-record.c | 886 +++++++++++++++++++++++++++++-- tools/perf/builtin-report.c | 3 + tools/perf/builtin-script.c | 31 +- tools/perf/builtin-stat.c | 23 +- tools/perf/perf.c | 1 + tools/perf/perf.h | 3 + tools/perf/tests/Build | 4 + tools/perf/tests/builtin-test.c | 16 + tools/perf/tests/dwarf-unwind.c | 4 +- tools/perf/tests/hists_common.c | 2 +- tools/perf/tests/hists_link.c | 2 +- tools/perf/tests/tests.h | 4 + tools/perf/tests/thread-comm.c | 48 ++ tools/perf/tests/thread-lookup-time.c | 181 +++++++ tools/perf/tests/thread-map-time.c | 91 ++++ tools/perf/tests/thread-mg-share.c | 7 +- tools/perf/tests/thread-mg-time.c | 94 ++++ tools/perf/ui/browsers/hists.c | 30 +- tools/perf/ui/gtk/hists.c | 3 + tools/perf/util/auxtrace.c | 30 +- tools/perf/util/auxtrace.h | 21 +- tools/perf/util/data.c | 64 +++ tools/perf/util/data.h | 5 + tools/perf/util/debug.c | 2 + tools/perf/util/debug.h | 1 + tools/perf/util/dso.c | 2 +- tools/perf/util/event.c | 139 ++++- tools/perf/util/evlist.c | 98 +++- tools/perf/util/evlist.h | 7 +- tools/perf/util/evsel.h | 15 + tools/perf/util/header.c | 93 +++- tools/perf/util/header.h | 18 +- tools/perf/util/hist.c | 4 +- tools/perf/util/intel-pt.c | 2 +- tools/perf/util/machine.c | 299 +++++++++-- tools/perf/util/machine.h | 22 +- tools/perf/util/map.c | 79 ++- tools/perf/util/map.h | 41 +- tools/perf/util/mmap.c | 11 +- tools/perf/util/mmap.h | 30 +- tools/perf/util/session.c | 178 ++++--- tools/perf/util/session.h | 5 +- tools/perf/util/stat.c | 5 +- tools/perf/util/stat.h | 5 +- tools/perf/util/symbol-elf.c | 2 +- tools/perf/util/symbol.c | 4 +- tools/perf/util/thread.c | 201 ++++++- tools/perf/util/thread.h | 29 +- tools/perf/util/tool.h | 7 +- tools/perf/util/unwind-libdw.c | 12 +- tools/perf/util/unwind-libunwind-local.c | 41 +- tools/perf/util/unwind-libunwind.c | 9 +- tools/perf/util/unwind.h | 7 +- tools/perf/util/util.c | 2 +- tools/perf/util/util.h | 2 + 62 files changed, 2599 insertions(+), 392 deletions(-) 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
Jiri Olsa
reviewerPATCH 1/49 perf tools: Remove perf_tool from event_op2
Now when we keep perf_tool pointer inside perf_session, there's no need to have perf_tool argument in the event_op2 callback. Removing it.
--- tools/perf/builtin-inject.c | 26 ++++++++---------- tools/perf/builtin-script.c | 31 ++++++++++----------- tools/perf/builtin-stat.c | 23 ++++++++-------- tools/perf/util/auxtrace.c | 10 +++---- tools/perf/util/auxtrace.h | 10 +++---- tools/perf/util/header.c | 16 +++++------ tools/perf/util/header.h | 15 ++++------ tools/perf/util/session.c | 67 +++++++++++++++++++-------------------------- tools/perf/util/session.h | 5 ++-- tools/perf/util/stat.c | 5 ++-- tools/perf/util/stat.h | 5 ++-- tools/perf/util/tool.h | 3 +- 12 files changed, 93 insertions(+), 123 deletions(-)
tools/perf/builtin-inject.c +11 -15
@@ -86,12 +86,10 @@ static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused, }#endif -static int perf_event__repipe_op2_synth (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session -__maybe_unused ) +static int perf_event__repipe_op2_synth (struct perf_session *session, +union perf_event *event) { -return perf_event__repipe_synth (tool, event); +return perf_event__repipe_synth (session->tool, event); } static int perf_event__repipe_attr(struct perf_tool *tool,
@@ -362,26 +360,24 @@ static int perf_event__repipe_exit(struct perf_tool *tool,return err; } -static int perf_event__repipe_tracing_data (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session) +static int perf_event__repipe_tracing_data (struct perf_session *session, +union perf_event *event) {int err; -perf_event__repipe_synth (tool, event); - err =perf_event__process_tracing_data (tool, event, session); +perf_event__repipe_synth (session->tool, event); + err =perf_event__process_tracing_data (session, event);return err; } -static int perf_event__repipe_id_index (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session) +static int perf_event__repipe_id_index (struct perf_session *session, +union perf_event *event) {int err; -perf_event__repipe_synth (tool, event); - err =perf_event__process_id_index (tool, event, session); +perf_event__repipe_synth (session->tool, event); + err =perf_event__process_id_index (session, event);return err; }
tools/perf/builtin-script.c +15 -16
@@ -2928,9 +2928,8 @@ static void script__setup_sample_type(struct perf_script *script) } } -static int process_stat_round_event (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session) +static int process_stat_round_event (struct perf_session *session, +union perf_event *event) {struct stat_round_event *round = &event->stat_round;struct perf_evsel *counter;
@@ -2944,9 +2943,8 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused,return 0 ; } -static int process_stat_config_event (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session__maybe_unused ) +static int process_stat_config_event (struct perf_session *session__maybe_unused , +union perf_event *event) {perf_event__read_stat_config (&stat_config, &event->stat_config);return 0 ;
@@ -2972,10 +2970,10 @@ static int set_maps(struct perf_script *script) }static -int process_thread_map_event (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session__maybe_unused ) +int process_thread_map_event (struct perf_session *session, +union perf_event *event) { +struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool);if (script->threads) {
@@ -2991,10 +2989,10 @@ int process_thread_map_event(struct perf_tool *tool, }static -int process_cpu_map_event (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session__maybe_unused ) +int process_cpu_map_event (struct perf_session *session, +union perf_event *event) { +struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool);if (script->cpus) {
@@ -3010,11 +3008,12 @@ int process_cpu_map_event(struct perf_tool *tool __maybe_unused, }#ifdef HAVE_AUXTRACE_SUPPORT -static int perf_script__process_auxtrace_info (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session) +static int perf_script__process_auxtrace_info (struct perf_session *session, +union perf_event *event) { -int ret =perf_event__process_auxtrace_info (tool, event, session); +struct perf_tool *tool = session->tool; + +int ret =perf_event__process_auxtrace_info (session, event);if (ret ==0 ) { struct perf_script *script = container_of(tool, struct perf_script, tool);
tools/perf/builtin-stat.c +11 -12
@@ -2450,9 +2450,8 @@ static int __cmd_record(int argc, const char **argv)return argc; } -static int process_stat_round_event (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session) +static int process_stat_round_event (struct perf_session *session, +union perf_event *event) {struct stat_round_event *stat_round = &event->stat_round;struct perf_evsel *counter;
@@ -2477,10 +2476,10 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused, }static -int process_stat_config_event (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session__maybe_unused ) +int process_stat_config_event (struct perf_session *session, +union perf_event *event) { +struct perf_tool *tool = session->tool; struct perf_stat *st = container_of(tool, struct perf_stat, tool);perf_event__read_stat_config (&stat_config, &event->stat_config);
@@ -2520,10 +2519,10 @@ static int set_maps(struct perf_stat *st) }static -int process_thread_map_event (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session__maybe_unused ) +int process_thread_map_event (struct perf_session *session, +union perf_event *event) { +struct perf_tool *tool = session->tool; struct perf_stat *st = container_of(tool, struct perf_stat, tool);if (st->threads) {
@@ -2539,10 +2538,10 @@ int process_thread_map_event(struct perf_tool *tool, }static -int process_cpu_map_event (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session__maybe_unused ) +int process_cpu_map_event (struct perf_session *session, +union perf_event *event) { +struct perf_tool *tool = session->tool; struct perf_stat *st = container_of(tool, struct perf_stat, tool);struct cpu_map *cpus;
tools/perf/util/auxtrace.c +4 -6
@@ -896,9 +896,8 @@ static bool auxtrace__dont_decode(struct perf_session *session) session->itrace_synth_opts->dont_decode; } -int perf_event__process_auxtrace_info (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session) +int perf_event__process_auxtrace_info (struct perf_session *session, +union perf_event *event) {enum auxtrace_type type = event->auxtrace_info.type;
@@ -1170,9 +1169,8 @@ void events_stats__auxtrace_error_warn(const struct events_stats *stats) } } -int perf_event__process_auxtrace_error (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session) +int perf_event__process_auxtrace_error (struct perf_session *session, +union perf_event *event) {if (auxtrace__dont_decode (session))return 0 ;
tools/perf/util/auxtrace.h +4 -6
@@ -513,15 +513,13 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process); -int perf_event__process_auxtrace_info (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +int perf_event__process_auxtrace_info (struct perf_session *session, +union perf_event *event);s64 perf_event__process_auxtrace (struct perf_tool *tool,union perf_event *event,struct perf_session *session); -int perf_event__process_auxtrace_error (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +int perf_event__process_auxtrace_error (struct perf_session *session, +union perf_event *event);int itrace_parse_synth_opts (const struct option *opt,const char *str,int unset);void itrace_synth_opts__set_default (struct itrace_synth_opts *synth_opts);
tools/perf/util/header.c +7 -9
@@ -3109,10 +3109,10 @@ int perf_event__synthesize_features(struct perf_tool *tool,return 0 ; } -int perf_event__process_feature (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session__maybe_unused ) +int perf_event__process_feature (struct perf_session *session, +union perf_event *event) { +struct perf_tool *tool = session->tool;struct feat_fd ff = { .fd =0 };struct feature_event *fe = (struct feature_event *)event;int type = fe->header.type;
@@ -3519,9 +3519,8 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,return aligned_size; } -int perf_event__process_tracing_data (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session) +int perf_event__process_tracing_data (struct perf_session *session, +union perf_event *event) {ssize_t size_read, padding, size = event->tracing_data.size;int fd =perf_data__fd (session->data);
@@ -3587,9 +3586,8 @@ int perf_event__synthesize_build_id(struct perf_tool *tool,return err; } -int perf_event__process_build_id (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session) +int perf_event__process_build_id (struct perf_session *session, +union perf_event *event) { __event_process_build_id(&event->build_id, event->build_id.filename,
tools/perf/util/header.h +6 -9
@@ -114,9 +114,8 @@ int perf_event__synthesize_extra_attr(struct perf_tool *tool,perf_event__handler_t process, bool is_pipe); -int perf_event__process_feature (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +int perf_event__process_feature (struct perf_session *session, +union perf_event *event); int perf_event__synthesize_attr(struct perf_tool *tool, struct perf_event_attr *attr, u32 ids, u64 *id,
@@ -146,17 +145,15 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp);int perf_event__synthesize_tracing_data (struct perf_tool *tool,int fd,struct perf_evlist *evlist,perf_event__handler_t process); -int perf_event__process_tracing_data (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +int perf_event__process_tracing_data (struct perf_session *session, +union perf_event *event);int perf_event__synthesize_build_id (struct perf_tool *tool,struct dso *pos,u16 misc,perf_event__handler_t process,struct machine *machine); -int perf_event__process_build_id (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +int perf_event__process_build_id (struct perf_session *session, +union perf_event *event);bool is_perf_magic (u64 magic);#define NAME_ALIGN 64
tools/perf/util/session.c +28 -39
142 lines not on this page · raw message
tools/perf/util/session.h +2 -3
@@ -120,9 +120,8 @@ int perf_session__deliver_synth_event(struct perf_session *session,union perf_event *event, struct perf_sample *sample); -int perf_event__process_id_index (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +int perf_event__process_id_index (struct perf_session *session, +union perf_event *event); int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process,
tools/perf/util/stat.c +2 -3
@@ -374,9 +374,8 @@ int perf_stat_process_counter(struct perf_stat_config *config,return 0 ; } -int perf_event__process_stat_event (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session) +int perf_event__process_stat_event (struct perf_session *session, +union perf_event *event) {struct perf_counts_values count;struct stat_event *st = &event->stat;
tools/perf/util/stat.h +2 -3
@@ -165,9 +165,8 @@ int perf_stat_process_counter(struct perf_stat_config *config,struct perf_tool ;union perf_event ;struct perf_session ; -int perf_event__process_stat_event (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +int perf_event__process_stat_event (struct perf_session *session, +union perf_event *event);size_t perf_event__fprintf_stat (union perf_event *event,FILE *fp);size_t perf_event__fprintf_stat_round (union perf_event *event,FILE *fp);
tools/perf/util/tool.h +1 -2
@@ -26,8 +26,7 @@ typedef int (*event_attr_op)(struct perf_tool *tool,union perf_event *event, struct perf_evlist **pevlist); -typedef int (*event_op2 )(struct perf_tool *tool,union perf_event *event, -struct perf_session *session); +typedef int (*event_op2 )(struct perf_session *session,union perf_event *event);typedef int (*event_oe )(struct perf_tool *tool,union perf_event *event,struct ordered_events *oe);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 2/49 perf tools: Remove perf_tool from event_op3
In reply to Jiri OlsaNow when we keep perf_tool pointer inside perf_session, there's no need to have perf_tool argument in the event_op3 callback. Removing it.
--- tools/perf/builtin-inject.c | 6 +++--- tools/perf/util/auxtrace.c | 7 +++---- tools/perf/util/auxtrace.h | 5 ++--- tools/perf/util/session.c | 8 +++----- tools/perf/util/tool.h | 4 +--- 5 files changed, 12 insertions(+), 18 deletions(-)
tools/perf/builtin-inject.c +3 -3
@@ -131,10 +131,10 @@ static int copy_bytes(struct perf_inject *inject, int fd, off_t size)return 0 ; } -static s64 perf_event__repipe_auxtrace (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session) +static s64 perf_event__repipe_auxtrace (struct perf_session *session, +union perf_event *event) { +struct perf_tool *tool = session->tool; struct perf_inject *inject = container_of(tool, struct perf_inject, tool);int ret;
tools/perf/util/auxtrace.c +3 -4
@@ -916,9 +916,8 @@ int perf_event__process_auxtrace_info(struct perf_session *session, } } -s64 perf_event__process_auxtrace (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session) +s64 perf_event__process_auxtrace (struct perf_session *session, +union perf_event *event) {s64 err;
@@ -934,7 +933,7 @@ s64 perf_event__process_auxtrace(struct perf_tool *tool,if (!session->auxtrace || event->header.type !=PERF_RECORD_AUXTRACE )return -EINVAL ; - err = session->auxtrace->process_auxtrace_event (session, event, tool); + err = session->auxtrace->process_auxtrace_event (session, event, session->tool);if (err <0 )return err;
tools/perf/util/auxtrace.h +2 -3
@@ -515,9 +515,8 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, perf_event__handler_t process);int perf_event__process_auxtrace_info (struct perf_session *session,union perf_event *event); -s64 perf_event__process_auxtrace (struct perf_tool *tool, -union perf_event *event, -struct perf_session *session); +s64 perf_event__process_auxtrace (struct perf_session *session, +union perf_event *event);int perf_event__process_auxtrace_error (struct perf_session *session,union perf_event *event); int itrace_parse_synth_opts(const struct option *opt, const char *str,
tools/perf/util/session.c +3 -5
@@ -275,10 +275,8 @@ static int skipn(int fd, off_t n)return 0 ; } -static s64 process_event_auxtrace_stub (struct perf_tool *tool__maybe_unused , -union perf_event *event, -struct perf_session *session -__maybe_unused ) +static s64 process_event_auxtrace_stub (struct perf_session *session__maybe_unused , +union perf_event *event) {dump_printf (": unhandled!\n" );if (perf_data__is_pipe (session->data))
@@ -1376,7 +1374,7 @@ static s64 perf_session__process_user_event(struct perf_session *session,case PERF_RECORD_AUXTRACE :/* setup for reading amidst mmap */ lseek (fd, file_offset + event->header.size,SEEK_SET ); -return tool->auxtrace (tool, event, session); +return tool->auxtrace (session, event);case PERF_RECORD_AUXTRACE_ERROR :perf_session__auxtrace_error_inc (session, event);return tool->auxtrace_error (session, event);
tools/perf/util/tool.h +1 -3
@@ -27,13 +27,11 @@ typedef int (*event_attr_op)(struct perf_tool *tool, struct perf_evlist **pevlist);typedef int (*event_op2 )(struct perf_session *session,union perf_event *event); +typedef s64 (*event_op3 )(struct perf_session *session,union perf_event *event);typedef int (*event_oe )(struct perf_tool *tool,union perf_event *event,struct ordered_events *oe); -typedef s64 (*event_op3 )(struct perf_tool *tool,union perf_event *event, -struct perf_session *session); -enum show_feature_header {SHOW_FEAT_NO_HEADER =0 ,SHOW_FEAT_HEADER ,
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 3/49 perf tools: Pass struct perf_mmap into auxtrace_mmap__read* functions
In reply to Jiri OlsaThe map struct will hold the file pointer, so we need to propagate it down the stack to record__write callers instead of its member the auxtrace_mmap struct.
--- tools/perf/builtin-record.c | 27 +++++++++++++-------------- tools/perf/util/auxtrace.c | 11 ++++++----- tools/perf/util/auxtrace.h | 5 +++-- 3 files changed, 22 insertions(+), 21 deletions(-)
tools/perf/builtin-record.c +13 -14
@@ -207,11 +207,11 @@ static int record__process_auxtrace(struct perf_tool *tool, }static int record__auxtrace_mmap_read (struct record *rec, -struct auxtrace_mmap *mm) +struct perf_mmap *map) {int ret; - ret =auxtrace_mmap__read (mm, rec->itr, &rec->tool, + ret =auxtrace_mmap__read (map, rec->itr, &rec->tool, record__process_auxtrace);if (ret <0 )return ret;
@@ -223,11 +223,11 @@ static int record__auxtrace_mmap_read(struct record *rec, }static int record__auxtrace_mmap_read_snapshot (struct record *rec, -struct auxtrace_mmap *mm) +struct perf_mmap *map) {int ret; - ret =auxtrace_mmap__read_snapshot (mm, rec->itr, &rec->tool, + ret =auxtrace_mmap__read_snapshot (map, rec->itr, &rec->tool, record__process_auxtrace, rec->opts.auxtrace_snapshot_size);if (ret <0 )
@@ -245,13 +245,12 @@ static int record__auxtrace_read_snapshot_all(struct record *rec)int rc =0 ;for (i =0 ; i < rec->evlist->nr_mmaps; i++) { -struct auxtrace_mmap *mm = - &rec->evlist->mmap[i].auxtrace_mmap; +struct perf_mmap *map = &rec->evlist->mmap[i]; -if (!mm->base) +if (!map->auxtrace_mmap.base)continue ; -if (record__auxtrace_mmap_read_snapshot (rec, mm) !=0 ) { +if (record__auxtrace_mmap_read_snapshot (rec, map) !=0 ) { rc =-1 ; goto out; }
@@ -277,7 +276,7 @@ static void record__read_auxtrace_snapshot(struct record *rec)static inline int record__auxtrace_mmap_read (struct record *rec__maybe_unused , -struct auxtrace_mmap *mm__maybe_unused ) +struct perf_mmap *map__maybe_unused ) {return 0 ; }
@@ -506,17 +505,17 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlireturn 0 ;for (i =0 ; i < evlist->nr_mmaps; i++) { -struct auxtrace_mmap *mm = &maps[i].auxtrace_mmap; +struct perf_mmap *map = &maps[i]; -if (maps[i].base) { -if (perf_mmap__push (&maps[i], overwrite, rec, record__pushfn) !=0 ) { +if (map->base) { +if (perf_mmap__push (map, overwrite, rec, record__pushfn) !=0 ) { rc =-1 ; goto out; } } -if (mm->base && !rec->opts.auxtrace_snapshot_mode && -record__auxtrace_mmap_read (rec, mm) !=0 ) { +if (map->auxtrace_mmap.base && !rec->opts.auxtrace_snapshot_mode && +record__auxtrace_mmap_read (rec, map) !=0 ) { rc =-1 ; goto out; }
tools/perf/util/auxtrace.c +6 -5
@@ -1178,11 +1178,12 @@ int perf_event__process_auxtrace_error(struct perf_session *session,return 0 ; } -static int __auxtrace_mmap__read (struct auxtrace_mmap *mm, +static int __auxtrace_mmap__read (struct perf_mmap *map,struct auxtrace_record *itr,struct perf_tool *tool,process_auxtrace_t fn,bool snapshot,size_t snapshot_size) { +struct auxtrace_mmap *mm = &map->auxtrace_mmap;u64 head, old = mm->prev, offset, ref;unsigned char *data = mm->base;size_t size, head_off, old_off, len1, len2, padding;
@@ -1288,18 +1289,18 @@ static int __auxtrace_mmap__read(struct auxtrace_mmap *mm,return 1 ; } -int auxtrace_mmap__read (struct auxtrace_mmap *mm,struct auxtrace_record *itr, +int auxtrace_mmap__read (struct perf_mmap *map,struct auxtrace_record *itr,struct perf_tool *tool,process_auxtrace_t fn) { -return __auxtrace_mmap__read (mm, itr, tool, fn, false,0 ); +return __auxtrace_mmap__read (map, itr, tool, fn, false,0 ); } -int auxtrace_mmap__read_snapshot (struct auxtrace_mmap *mm, +int auxtrace_mmap__read_snapshot (struct perf_mmap *map,struct auxtrace_record *itr,struct perf_tool *tool,process_auxtrace_t fn,size_t snapshot_size) { -return __auxtrace_mmap__read (mm, itr, tool, fn, true, snapshot_size); +return __auxtrace_mmap__read (map, itr, tool, fn, true, snapshot_size); }/**
tools/perf/util/auxtrace.h +3 -2
@@ -33,6 +33,7 @@ union perf_event;struct perf_session ;struct perf_evlist ;struct perf_tool ; +struct perf_mmap ;struct option ;struct record_opts ;struct auxtrace_info_event ;
@@ -433,10 +434,10 @@ typedef int (*process_auxtrace_t)(struct perf_tool *tool, union perf_event *event, void *data1, size_t len1, void *data2, size_t len2); -int auxtrace_mmap__read (struct auxtrace_mmap *mm,struct auxtrace_record *itr, +int auxtrace_mmap__read (struct perf_mmap *map,struct auxtrace_record *itr,struct perf_tool *tool,process_auxtrace_t fn); -int auxtrace_mmap__read_snapshot (struct auxtrace_mmap *mm, +int auxtrace_mmap__read_snapshot (struct perf_mmap *map,struct auxtrace_record *itr,struct perf_tool *tool,process_auxtrace_t fn,size_t snapshot_size);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 4/49 perf tools: Add struct perf_mmap arg into record__write
In reply to Jiri OlsaThe map argument will hold the file pointer
to write the data to.
--- tools/perf/builtin-record.c | 24 ++++++++++++++---------- tools/perf/util/auxtrace.c | 2 +- tools/perf/util/auxtrace.h | 1 + tools/perf/util/mmap.c | 8 ++++---- tools/perf/util/mmap.h | 4 ++-- 5 files changed, 22 insertions(+), 17 deletions(-)
tools/perf/builtin-record.c +14 -10
@@ -106,9 +106,12 @@ static bool switch_output_time(struct record *rec)trigger_is_ready (&switch_output_trigger); } -static int record__write (struct record *rec,void *bf,size_t size) +static int record__write (struct record *rec,struct perf_mmap *map__maybe_unused , +void *bf,size_t size) { -if (perf_data__write (rec->session->data, bf, size) <0 ) { +struct perf_data_file *file = &rec->session->data->file; + +if (perf_data_file__write (file, bf, size) <0 ) {pr_err ("failed to write perf data, error: %m\n" );return -1 ; }
@@ -127,15 +130,15 @@ static int process_synthesized_event(struct perf_tool *tool, struct machine *machine __maybe_unused) { struct record *rec = container_of(tool, struct record, tool); -return record__write (rec, event, event->header.size); +return record__write (rec,NULL , event, event->header.size); } -static int record__pushfn (void *to,void *bf,size_t size) +static int record__pushfn (struct perf_mmap *map,void *to,void *bf,size_t size) {struct record *rec = to; rec->samples++; -return record__write (rec, bf, size); +return record__write (rec, map, bf, size); }static volatile int done;
@@ -170,6 +173,7 @@ static void record__sig_exit(void)#ifdef HAVE_AUXTRACE_SUPPORT static int record__process_auxtrace (struct perf_tool *tool, +struct perf_mmap *map,union perf_event *event,void *data1,size_t len1,void *data2,size_t len2) {
@@ -197,11 +201,11 @@ static int record__process_auxtrace(struct perf_tool *tool,if (padding) padding =8 - padding; -record__write (rec, event, event->header.size); -record__write (rec, data1, len1); +record__write (rec, map, event, event->header.size); +record__write (rec, map, data1, len1);if (len2) -record__write (rec, data2, len2); -record__write (rec, &pad, padding); +record__write (rec, map, data2, len2); +record__write (rec, map, &pad, padding);return 0 ; }
@@ -526,7 +530,7 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli* at least one event. */ if (bytes_written != rec->bytes_written) - rc =record__write (rec, &finished_round_event,sizeof (finished_round_event)); + rc =record__write (rec,NULL , &finished_round_event,sizeof (finished_round_event));if (overwrite)perf_evlist__toggle_bkw_mmap (evlist,BKW_MMAP_EMPTY );
tools/perf/util/auxtrace.c +1 -1
@@ -1270,7 +1270,7 @@ static int __auxtrace_mmap__read(struct perf_mmap *map, ev.auxtrace.tid = mm->tid; ev.auxtrace.cpu = mm->cpu; -if (fn (tool, &ev, data1, len1, data2, len2)) +if (fn (tool, map, &ev, data1, len1, data2, len2))return -1 ; mm->prev = head;
tools/perf/util/auxtrace.h +1 -0
@@ -431,6 +431,7 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, bool per_cpu);typedef int (*process_auxtrace_t )(struct perf_tool *tool, +struct perf_mmap *map,union perf_event *event,void *data1,size_t len1,void *data2,size_t len2);
tools/perf/util/mmap.c +4 -4
@@ -267,8 +267,8 @@ static int overwrite_rb_find_range(void *buf, int mask, u64 head, u64 *start, u6return -1 ; } -int perf_mmap__push (struct perf_mmap *md,bool overwrite, -void *to,int push (void *to,void *buf,size_t size)) +int perf_mmap__push (struct perf_mmap *md,bool overwrite,void *to, +int push (struct perf_mmap *map,void *to,void *buf,size_t size)) {u64 head =perf_mmap__read_head (md);u64 old = md->prev;
@@ -307,7 +307,7 @@ int perf_mmap__push(struct perf_mmap *md, bool overwrite, size = md->mask +1 - (start & md->mask); start += size; -if (push (to, buf, size) <0 ) { +if (push (md, to, buf, size) <0 ) { rc =-1 ; goto out; }
@@ -317,7 +317,7 @@ int perf_mmap__push(struct perf_mmap *md, bool overwrite, size = end - start; start += size; -if (push (to, buf, size) <0 ) { +if (push (md, to, buf, size) <0 ) { rc =-1 ; goto out; }
tools/perf/util/mmap.h +2 -2
@@ -89,8 +89,8 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail)union perf_event *perf_mmap__read_forward (struct perf_mmap *map);union perf_event *perf_mmap__read_backward (struct perf_mmap *map); -int perf_mmap__push (struct perf_mmap *md,bool backward, -void *to,int push (void *to,void *buf,size_t size)); +int perf_mmap__push (struct perf_mmap *md,bool overwrite,void *to, +int push (struct perf_mmap *map,void *to,void *buf,size_t size));size_t perf_mmap__mmap_len (struct perf_mmap *map);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 5/49 perf tools: Use a software dummy event to track task/mmap events
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
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 just 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
@@ -266,6 +266,36 @@ int perf_evlist__add_dummy(struct perf_evlist *evlist)return 0 ; } +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
@@ -77,6 +77,7 @@ static inline int perf_evlist__add_default(struct perf_evlist *evlist)return __perf_evlist__add_default (evlist, true); } +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
@@ -390,6 +390,21 @@ static inline bool perf_evsel__is_bpf_output(struct perf_evsel *evsel) (attr->type ==PERF_TYPE_SOFTWARE ); } +/** +* 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.13.6
Jiri Olsa
reviewerPATCH 6/49 perf tools: Create separate mmap for dummy tracking event
In reply to Jiri OlsaWhen 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 | 9 ++++++++ tools/perf/util/evlist.c | 55 ++++++++++++++++++++++++++++++++++++--------- tools/perf/util/evlist.h | 3 +++ tools/perf/util/mmap.h | 1 + 4 files changed, 58 insertions(+), 10 deletions(-)
tools/perf/builtin-record.c +9 -0
@@ -510,6 +510,8 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlifor (i =0 ; i < evlist->nr_mmaps; i++) {struct perf_mmap *map = &maps[i]; +struct perf_mmap *track_map = evlist->track_mmap ? + &evlist->track_mmap[i] :NULL ;if (map->base) {if (perf_mmap__push (map, overwrite, rec, record__pushfn) !=0 ) {
@@ -523,6 +525,13 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli rc =-1 ; goto out; } + +if (track_map && track_map->base) { +if (perf_mmap__push (track_map, overwrite, rec, record__pushfn) !=0 ) { + rc =-1 ; + goto out; + } + } }/*
tools/perf/util/evlist.c +45 -10
111 lines not on this page · raw message
tools/perf/util/evlist.h +3 -0
@@ -25,6 +25,8 @@ struct record_opts;#define PERF_EVLIST__HLIST_BITS 8#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) +#define TRACK_MMAP_SIZE (((128 * 1024 / page_size) + 1) * page_size) +struct perf_evlist {struct list_head entries;struct hlist_head heads[PERF_EVLIST__HLIST_SIZE ];
@@ -45,6 +47,7 @@ struct perf_evlist {struct fdarray pollfd;struct perf_mmap *mmap;struct perf_mmap *overwrite_mmap; +struct perf_mmap *track_mmap;struct thread_map *threads;struct cpu_map *cpus;struct perf_evsel *selected;
tools/perf/util/mmap.h +1 -0
@@ -55,6 +55,7 @@ enum bkw_mmap_state {struct mmap_params {int prot, mask;struct auxtrace_mmap_params auxtrace_mp; +bool track_mmap; };int perf_mmap__mmap (struct perf_mmap *map,struct mmap_params *mp,int fd);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 7/49 perf tools: Extend perf_evlist__mmap_ex() to use track mmap
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
The perf_evlist__mmap_ex function creates data and auxtrace mmaps and optionally tracking mmaps for events now. 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 | 7 ++++--- tools/perf/util/evlist.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-)
tools/perf/builtin-record.c +1 -1
@@ -306,7 +306,7 @@ static int record__mmap_evlist(struct record *rec,if (perf_evlist__mmap_ex (evlist, opts->mmap_pages, 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 +4 -3
@@ -1094,6 +1094,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
@@ -1106,7 +1107,7 @@ 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,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;
@@ -1117,7 +1118,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,* So &mp should not be passed through const pointer. */ struct mmap_params mp = { - .track_mmap = false, + .track_mmap = use_track_mmap, };if (!evlist->mmap) {
@@ -1162,7 +1163,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,int perf_evlist__mmap (struct perf_evlist *evlist,unsigned int pages) { -return perf_evlist__mmap_ex (evlist, pages,0 , false); +return perf_evlist__mmap_ex (evlist, pages,0 , false, false); }int perf_evlist__create_maps (struct perf_evlist *evlist,struct target *target)
tools/perf/util/evlist.h +1 -1
@@ -175,7 +175,7 @@ unsigned long perf_event_mlock_kb_in_pages(void);int perf_evlist__mmap_ex (struct perf_evlist *evlist,unsigned int pages,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);void perf_evlist__munmap (struct perf_evlist *evlist);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 8/49 perf report: Skip dummy tracking event
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
The 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
@@ -433,6 +433,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
@@ -3267,14 +3267,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; }
@@ -3291,7 +3294,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, },
@@ -3318,21 +3321,22 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, struct perf_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_entry (evlist, pos) { +if (perf_evsel__is_dummy_tracking (pos)) +continue ;if (perf_evsel__is_group_leader (pos)) nr_entries++; }
@@ -3341,6 +3345,20 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, goto single_entry; } +evlist__for_each_entry (evlist, pos) { +if (perf_evsel__is_dummy_tracking (pos)) + nr_entries--; + } + +if (nr_entries ==1 ) { +evlist__for_each_entry (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
@@ -638,6 +638,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.13.6
Jiri Olsa
reviewerPATCH 9/49 perf tools: Make copyfile_offset global
In reply to Jiri OlsaIt will be used outside of util object in following patches.
--- tools/perf/util/util.c | 2 +- tools/perf/util/util.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-)
tools/perf/util/util.c +1 -1
@@ -189,7 +189,7 @@ static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)return err; } -static int copyfile_offset (int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size) +int copyfile_offset (int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size) {void *ptr;loff_t pgoff;
tools/perf/util/util.h +2 -0
@@ -68,4 +68,6 @@ extern bool perf_singlethreaded;void perf_set_singlethreaded (void );void perf_set_multithreaded (void ); +int copyfile_offset (int ifd,loff_t off_in,int ofd,loff_t off_out,u64 size); +#endif /* GIT_COMPAT_UTIL_H */
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 10/49 perf tools: Add HEADER_DATA_INDEX feature
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
The 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 | 1 + tools/perf/util/header.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 ++ 3 files changed, 80 insertions(+)
tools/perf/builtin-record.c +1 -0
@@ -579,6 +579,7 @@ static void record__init_features(struct record *rec)perf_header__clear_feat (&session->header,HEADER_AUXTRACE );perf_header__clear_feat (&session->header,HEADER_STAT ); +perf_header__clear_feat (&session->header,HEADER_DATA_INDEX ); } static void
tools/perf/util/header.c +76 -0
106 lines not on this page · raw message
tools/perf/util/header.h +3 -0
@@ -36,6 +36,7 @@ enum {HEADER_STAT ,HEADER_CACHE ,HEADER_SAMPLE_TIME , +HEADER_DATA_INDEX ,HEADER_LAST_FEATURE , HEADER_FEAT_BITS = 256, };
@@ -76,6 +77,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_env env;
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 11/49 perf tools: Handle indexed data file properly
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
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.
--- tools/perf/builtin-record.c | 2 ++ tools/perf/perf.c | 1 + tools/perf/perf.h | 2 ++ tools/perf/util/header.c | 1 + tools/perf/util/session.c | 52 ++++++++++++++++++++++++++++++++++++--------- 5 files changed, 48 insertions(+), 10 deletions(-)
tools/perf/builtin-record.c +2 -0
@@ -52,6 +52,8 @@#include <sys/mman.h> #include <sys/wait.h> #include <linux/time64.h> +#include <sys/types.h> +#include <sys/stat.h> struct switch_output {bool enabled;
tools/perf/perf.c +1 -0
@@ -39,6 +39,7 @@ const char perf_more_info_string[] =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
@@ -34,6 +34,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/header.c +1 -0
@@ -2277,6 +2277,7 @@ static int process_data_index(struct feat_fd *ff, void *data __maybe_unused) ph->index = idx; ph->nr_index = nr_idx; + perf_has_index = true;return 0 ; }
tools/perf/util/session.c +42 -10
77 lines not on this page · raw message
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 12/49 perf tools: Add perf_data__create_index function
In reply to Jiri OlsaAdding perf_data__create_index function to create
and open index files within perf_data struct.
--- tools/perf/util/data.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/data.h | 5 ++++ 2 files changed, 69 insertions(+)
tools/perf/util/data.c +64 -0
@@ -197,3 +197,67 @@ int perf_data__switch(struct perf_data *data,free (new_filepath);return ret; } + +static void free_index (struct perf_data_file *index,int nr) +{ +while (--nr >=1 ) { +close (index[nr].fd); +free ((char *) index[nr].path); + } +free (index); +} + +static void clean_index (struct perf_data *data, +struct perf_data_file *index, +int index_nr) +{ +char path[PATH_MAX ]; + +scnprintf (path,sizeof (path),"%s.dir" , data->file.path); +rm_rf (path); + +free_index (index, index_nr); +} + +void perf_data__clean_index (struct perf_data *data) +{ +clean_index (data, data->index, data->index_nr); +} + +int perf_data__create_index (struct perf_data *data,int nr) +{ +struct perf_data_file *index; +char path[PATH_MAX ]; +int ret =-1 , i =0 ; + + index =malloc (nr *sizeof (*index)); +if (!index) +return -ENOMEM ; + + data->index = index; + data->index_nr = nr; + +scnprintf (path,sizeof (path),"%s.dir" , data->file.path); +if (rm_rf (path) <0 ||mkdir (path,S_IRWXU ) <0 ) + goto out_err; + +for (; i < nr; i++) { +struct perf_data_file *file = &index[i]; + +if (asprintf ((char **) &file->path,"%s.dir/perf.data.%d" , + data->file.path, i) <0 ) + goto out_err; + + ret =open (file->path,O_RDWR |O_CREAT |O_TRUNC ,S_IRUSR |S_IWUSR ); +if (ret <0 ) + goto out_err; + + file->fd = ret; + } + +return 0 ; + +out_err: +clean_index (data, index, i); +return ret; +}
tools/perf/util/data.h +5 -0
@@ -20,6 +20,8 @@ struct perf_data {bool force;unsigned long size;enum perf_data_mode mode; +struct perf_data_file *index; +int index_nr; };static inline bool perf_data__is_read (struct perf_data *data)
@@ -63,4 +65,7 @@ ssize_t perf_data_file__write(struct perf_data_file *file,int perf_data__switch (struct perf_data *data,const char *postfix,size_t pos,bool at_exit); +int perf_data__create_index (struct perf_data *data, +int nr); +void perf_data__clean_index (struct perf_data *data);#endif /* __PERF_DATA_H */
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 13/49 perf record: Add --index option for building index table
In reply to Jiri OlsaThe 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 | 134 +++++++++++++++++++++++++++++-- tools/perf/perf.h | 1 + tools/perf/util/mmap.h | 15 ++-- 4 files changed, 142 insertions(+), 12 deletions(-)
tools/perf/Documentation/perf-record.txt +4 -0
@@ -486,6 +486,10 @@ config terms. For example: 'cycles/overwrite/' and 'instructions/no-overwrite/'. Implies --tail-synthesize. +--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 +129 -5
195 lines not on this page · raw message
tools/perf/perf.h +1 -0
@@ -62,6 +62,7 @@ struct record_opts {bool tail_synthesize;bool overwrite;bool ignore_missing_thread; +bool index;unsigned int freq;unsigned int mmap_pages;unsigned int auxtrace_mmap_pages;
tools/perf/util/mmap.h +8 -7
@@ -15,13 +15,14 @@* @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this */ struct perf_mmap { -void *base; -int mask; -int fd; -refcount_t refcnt; -u64 prev; -struct auxtrace_mmap auxtrace_mmap; -char event_copy[PERF_SAMPLE_MAX_SIZE ]__aligned(8) ; +void *base; +int mask; +int fd; +refcount_t refcnt; +u64 prev; +struct auxtrace_mmap auxtrace_mmap; +struct perf_data_file *file; +char event_copy[PERF_SAMPLE_MAX_SIZE ]__aligned(8) ; };/*
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 14/49 perf tools: Introduce thread__comm(_str)_by_time() helpers
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
When 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
@@ -192,6 +192,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); +} +static int ____thread__set_comm (struct thread *thread,const char *str,u64 timestamp,bool exec) {
@@ -206,7 +221,13 @@ static int ____thread__set_comm(struct thread *thread, const char *str, 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);
@@ -266,6 +287,16 @@ const char *thread__comm_str(const struct thread *thread)return str; } +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
@@ -86,8 +86,10 @@ int thread__set_comm_from_proc(struct thread *thread);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);int thread__insert_map (struct thread *thread,struct map *map); +const char *thread__comm_str_by_time (const struct thread *thread,u64 timestamp);int thread__fork (struct thread *thread,struct thread *parent,u64 timestamp);size_t thread__fprintf (struct thread *thread,FILE *fp);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 15/49 perf tools: Add a test case for thread comm handling
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
The new test case checks various thread comm handling APIs like overridding and time sorting.
--- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 ++++ tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-comm.c | 48 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 tools/perf/tests/thread-comm.c
tools/perf/tests/Build +1 -0
@@ -23,6 +23,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
@@ -271,6 +271,10 @@ static struct test generic_tests[] = { .func = test__unit_number__scnprint, }, { + .desc = "Test thread comm handling", + .func = test__thread_comm, + }, + { .func = NULL, }, };
tools/perf/tests/tests.h +1 -0
@@ -102,6 +102,7 @@ int test__clang(struct test *test, int subtest);const char *test__clang_subtest_get_desc (int subtest);int test__clang_subtest_get_nr (void );int test__unit_number__scnprint (struct test *test,int subtest); +int test__thread_comm (struct test *test,int subtest);bool test__bp_signal_is_supported (void );
tools/perf/tests/thread-comm.c new file +48 -0
48 lines not on this page · raw message
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 16/49 perf tools: Use thread__comm_by_time() when adding hist entries
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
Now thread->comm can be handled with time properly, use it to find the correct comm at the time when adding hist entries.
--- tools/perf/util/hist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/hist.c +2 -2
@@ -588,7 +588,7 @@ __hists__add_entry(struct hists *hists,struct namespaces *ns =thread__namespaces (al->thread);struct hist_entry entry = { .thread = al->thread, - .comm =thread__comm (al->thread), + .comm =thread__comm_by_time (al->thread, sample->time), .cgroup_id = { .dev = ns ? ns->link_info[CGROUP_NS_INDEX ].dev :0 , .ino = ns ? ns->link_info[CGROUP_NS_INDEX ].ino :0 ,
@@ -946,7 +946,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, .hists = evsel__hists(evsel), .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,
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 17/49 perf tools: Convert dead thread list into rbtree
In reply to Jiri OlsaCurrently 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-thread 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/intel-pt.c | 2 +- tools/perf/util/machine.c | 82 +++++++++++++++++++++++++++++++++++++++++----- tools/perf/util/machine.h | 10 +++--- tools/perf/util/thread.c | 12 ++++++- tools/perf/util/thread.h | 6 ++-- 5 files changed, 93 insertions(+), 19 deletions(-)
tools/perf/util/intel-pt.c +1 -1
@@ -2535,7 +2535,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,* current thread lifetime assuption is kept and we don't segfault * at list_del_init(). */ -INIT_LIST_HEAD (&pt->unknown_thread->node); +INIT_LIST_HEAD (&pt->unknown_thread->tid_list); err =thread__set_comm (pt->unknown_thread,"unknown" ,0 );if (err)
tools/perf/util/machine.c +74 -8
139 lines not on this page · raw message
tools/perf/util/machine.h +5 -5
@@ -29,11 +29,11 @@ struct vdso_info;#define THREADS__TABLE_SIZE (1 << THREADS__TABLE_BITS)struct threads { -struct rb_root entries; -struct rw_semaphore lock; -unsigned int nr; -struct list_head dead; -struct thread *last_match; +struct rb_root entries; +struct rb_root dead; +struct rw_semaphore lock; +unsigned int nr; +struct thread *last_match; };struct machine {
tools/perf/util/thread.c +11 -1
@@ -61,6 +61,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)list_add (&comm->list, &thread->comm_list);refcount_set (&thread->refcnt,1 ); +INIT_LIST_HEAD (&thread->tid_list);RB_CLEAR_NODE (&thread->rb_node);/* Thread holds first ref to nsdata. */ thread->nsinfo =nsinfo__new (pid);
@@ -79,6 +80,7 @@ void thread__delete(struct thread *thread)struct comm *comm, *tmp_comm;BUG_ON (!RB_EMPTY_NODE (&thread->rb_node)); +BUG_ON (!list_empty (&thread->tid_list));thread_stack__free (thread);
@@ -123,7 +125,15 @@ void thread__put(struct thread *thread)* Remove it from the dead_threads list, as last reference * is gone. */ -list_del_init (&thread->node); +if (!RB_EMPTY_NODE (&thread->rb_node)) { +struct machine *machine = thread->mg->machine; +struct threads *threads =machine__threads (machine, thread->tid); + +rb_erase (&thread->rb_node, &threads->dead); +RB_CLEAR_NODE (&thread->rb_node); + } + +list_del_init (&thread->tid_list);thread__delete (thread); } }
tools/perf/util/thread.h +2 -4
@@ -16,10 +16,8 @@ struct thread_stack;struct unwind_libunwind_ops ;struct thread { -union { -struct rb_node rb_node; -struct list_head node; - }; +struct rb_node rb_node; +struct list_head tid_list;struct map_groups *mg;pid_t pid_;/* Not all tools update this */ pid_t tid;
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 18/49 perf tools: Introduce machine__find*_thread_by_time()
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
With 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 (i.e. recent) thread rbtree and then dead thread tree (and tid list). If it couldn't find anyone, it'll create a new (missing) 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 | 4 +- tools/perf/tests/hists_common.c | 2 +- tools/perf/tests/hists_link.c | 2 +- tools/perf/util/event.c | 6 +- tools/perf/util/machine.c | 128 +++++++++++++++++++++++++++++++++++++++- tools/perf/util/machine.h | 10 +++- tools/perf/util/thread.c | 5 ++ tools/perf/util/thread.h | 1 + 8 files changed, 148 insertions(+), 10 deletions(-)
tools/perf/tests/dwarf-unwind.c +1 -3
@@ -79,12 +79,10 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) static noinline 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 +1 -1
@@ -104,7 +104,7 @@ struct machine *setup_fake_machine(struct machines *machines)for (i =0 ; i <ARRAY_SIZE (fake_mmap_info); i++) {struct perf_sample sample = { - .cpumode =PERF_RECORD_MISC_USER , + .cpumode =PERF_RECORD_MISC_USER , .time =-1ULL , };union perf_event fake_mmap_event = { .mmap = {
tools/perf/tests/hists_link.c +1 -1
@@ -67,7 +67,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 , .weight =1 , }; +struct perf_sample sample = { .period =1 , .weight =1 , .time =-1ULL , };size_t i =0 , k;/*
tools/perf/util/event.c +4 -2
@@ -21,6 +21,7 @@#include "thread.h" #include "thread_map.h" #include "sane_ctype.h" +#include "session.h" #include "symbol/kallsyms.h" #include "asm/bug.h" #include "stat.h"
@@ -1584,9 +1585,10 @@ void thread__find_addr_location(struct thread *thread,int machine__resolve (struct machine *machine,struct addr_location *al,struct perf_sample *sample) { -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 +127 -1
146 lines not on this page · raw message
tools/perf/util/machine.h +8 -2
@@ -91,8 +91,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);
@@ -185,6 +183,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);struct dso *machine__findnew_dso (struct machine *machine,const char *filename);
tools/perf/util/thread.c +5 -0
@@ -225,6 +225,10 @@ static int ____thread__set_comm(struct thread *thread, const char *str,/* Override the default :tid entry */ if (!thread->comm_set) {int err =comm__override (curr, str, timestamp, exec); + +if (!thread->start_time) + thread->start_time = timestamp; +if (err)return err; }else {
@@ -409,6 +413,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
@@ -33,6 +33,7 @@ struct thread {struct list_head comm_list;struct rw_semaphore comm_lock;u64 db_id; +u64 start_time;void *priv;struct thread_stack *ts;
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 19/49 perf tools: Add thread::exited flag
In reply to Jiri OlsaAdding thread::exited to indicate the thread has exited, and keeping the thread::dead flag to indicate thread is on the dead list.
--- tools/perf/util/machine.c | 3 +++ tools/perf/util/thread.h | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-)
tools/perf/util/machine.c +3 -0
@@ -1667,6 +1667,9 @@ static void __machine__remove_thread(struct machine *machine, struct thread *th,rb_erase_init (&th->rb_node, &threads->entries);RB_CLEAR_NODE (&th->rb_node); --threads->nr; + + th->dead = true; +/* * No need to have an additional reference for non-index file * as they can be released when reference holders died and
tools/perf/util/thread.h +3 -2
@@ -27,7 +27,8 @@ struct thread {char shortname[3 ];bool comm_set;int comm_len; -bool dead;/* if set thread has exited */ +bool exited;/* if set thread has exited */ +bool dead;/* if set thread is in dead tree */ struct list_head namespaces_list;struct rw_semaphore namespaces_lock;struct list_head comm_list;
@@ -65,7 +66,7 @@ static inline void __thread__zput(struct thread **thread)static inline void thread__exited (struct thread *thread) { - thread->dead = true; + thread->exited = true; }struct namespaces *thread__namespaces (const struct thread *thread);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 20/49 perf tools: Add a test case for timed thread handling
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
A 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 | 181 ++++++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 tools/perf/tests/thread-lookup-time.c
tools/perf/tests/Build +1 -0
@@ -25,6 +25,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
@@ -275,6 +275,10 @@ static struct test generic_tests[] = { .func = test__thread_comm, }, { + .desc = "Test thread lookup with time", + .func = test__thread_lookup_time, + }, + { .func = NULL, }, };
tools/perf/tests/tests.h +1 -0
@@ -103,6 +103,7 @@ const char *test__clang_subtest_get_desc(int subtest);int test__clang_subtest_get_nr (void );int test__unit_number__scnprint (struct test *test,int subtest);int test__thread_comm (struct test *test,int subtest); +int test__thread_lookup_time (struct test *test,int subtest);bool test__bp_signal_is_supported (void );
tools/perf/tests/thread-lookup-time.c new file +181 -0
181 lines not on this page · raw message
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 21/49 perf tools: Maintain map groups list in a leader thread
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
To 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 | 15 ++++- tools/perf/util/map.c | 3 + tools/perf/util/map.h | 2 + tools/perf/util/thread.c | 117 ++++++++++++++++++++++++++++++++++++- tools/perf/util/thread.h | 3 + 7 files changed, 142 insertions(+), 7 deletions(-)
tools/perf/tests/thread-mg-share.c +5 -2
@@ -24,6 +24,9 @@ int test__thread_mg_share(struct test *test __maybe_unused, int subtest __maybe_* 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) */
@@ -44,7 +47,7 @@ int test__thread_mg_share(struct test *test __maybe_unused, int subtest __maybe_ leader && t1 && t2 && t3 && other); mg = leader->mg; -TEST_ASSERT_EQUAL ("wrong refcnt" ,refcount_read (&mg->refcnt),4 ); +TEST_ASSERT_EQUAL ("wrong refcnt" ,refcount_read (&mg->refcnt),5 );/* test the map groups pointer is shared */ TEST_ASSERT_VAL ("map groups don't match" , mg == t1->mg);
@@ -72,7 +75,7 @@ int test__thread_mg_share(struct test *test __maybe_unused, int subtest __maybe_machine__remove_thread (machine, other_leader); other_mg = other->mg; -TEST_ASSERT_EQUAL ("wrong refcnt" ,refcount_read (&other_mg->refcnt),2 ); +TEST_ASSERT_EQUAL ("wrong refcnt" ,refcount_read (&other_mg->refcnt),3 );TEST_ASSERT_VAL ("map groups don't match" , other_mg == other_leader->mg);
tools/perf/util/event.c +2 -0
@@ -1510,6 +1510,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 +13 -2
@@ -395,8 +395,19 @@ static void machine__update_thread_pid(struct machine *machine,if (!leader) goto out_err; -if (!leader->mg) - leader->mg =map_groups__new (machine); +if (!leader->mg) { +struct map_groups *mg =map_groups__new (machine); + +if (mg ==NULL ) { +pr_err ("Not enough memory for map groups\n" ); +return ; + } + +if (thread__set_map_groups (leader, mg,0 ) <0 ) { +map_groups__put (mg); + goto out_err; + } + }if (!leader->mg) goto out_err;
tools/perf/util/map.c +3 -0
@@ -500,6 +500,8 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) } mg->machine = machine;refcount_set (&mg->refcnt,1 ); + mg->timestamp =0 ; +INIT_LIST_HEAD (&mg->list); }static void __maps__purge (struct maps *maps)
@@ -556,6 +558,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
@@ -70,6 +70,8 @@ struct map_groups {struct maps maps[MAP__NR_TYPES ];struct machine *machine;refcount_t refcnt; +u64 timestamp; +struct list_head list; };struct map_groups *map_groups__new (struct machine *machine);
tools/perf/util/thread.c +114 -3
164 lines not on this page · raw message
tools/perf/util/thread.h +3 -0
@@ -19,6 +19,7 @@ struct thread {struct rb_node rb_node;struct list_head tid_list;struct map_groups *mg; +struct list_head mg_list;pid_t pid_;/* Not all tools update this */ pid_t tid;pid_t ppid;
@@ -90,6 +91,8 @@ struct comm *thread__comm_by_time(const struct thread *thread, u64 timestamp);const char *thread__comm_str (const struct thread *thread);int thread__insert_map (struct thread *thread,struct map *map);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);int thread__fork (struct thread *thread,struct thread *parent,u64 timestamp);size_t thread__fprintf (struct thread *thread,FILE *fp);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 22/49 perf tools: Introduce thread__find_addr_location_by_time() and friends
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
These 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 | 58 +++++++++++++++++++++++++++++++++++++++-------- tools/perf/util/machine.c | 24 ++++++++++++-------- tools/perf/util/thread.c | 26 +++++++++++++++++++++ tools/perf/util/thread.h | 11 +++++++++ 4 files changed, 101 insertions(+), 18 deletions(-)
tools/perf/util/event.c +49 -9
@@ -1491,16 +1491,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 ;
@@ -1569,6 +1567,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)
@@ -1580,6 +1601,22 @@ 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); +else + al->sym =NULL ; +} +/* * Callers need to drop the reference to al->thread, obtained in * machine__findnew_thread()
@@ -1606,7 +1643,9 @@ int machine__resolve(struct machine *machine, struct addr_location *al, machine__kernel_map(machine) == NULL)machine__create_kernel_maps (machine); -thread__find_addr_map (thread, sample->cpumode,MAP__FUNCTION , sample->ip, al); +thread__find_addr_map_by_time (thread, sample->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>" );
@@ -1685,10 +1724,11 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr)void thread__resolve (struct thread *thread,struct addr_location *al,struct perf_sample *sample) { -thread__find_addr_map (thread, sample->cpumode,MAP__FUNCTION , sample->addr, al); +thread__find_addr_map_by_time (thread, sample->cpumode,MAP__FUNCTION , + sample->addr, al, sample->time);if (!al->map) -thread__find_addr_map (thread, sample->cpumode,MAP__VARIABLE , - sample->addr, al); +thread__find_addr_map_by_time (thread, sample->cpumode,MAP__VARIABLE , + sample->addr, al, sample->time); al->cpu = sample->cpu; al->sym =NULL ;
tools/perf/util/machine.c +15 -9
@@ -1850,7 +1850,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;
@@ -1862,7 +1862,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;
@@ -1873,20 +1874,23 @@ static void ip__resolve_ams(struct thread *thread,static void ip__resolve_data (struct thread *thread,u8 m,struct addr_map_symbol *ams, -u64 addr,u64 phys_addr) +u64 addr,u64 phys_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;
@@ -1904,9 +1908,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_ams (al->thread, &mi->iaddr, sample->ip, sample->time);ip__resolve_data (al->thread, al->cpumode, &mi->daddr, - sample->addr, sample->phys_addr); + sample->addr, sample->phys_addr, sample->time); mi->data_src.val = sample->data_src;return mi;
@@ -2024,8 +2028,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/thread.c +26 -0
@@ -557,3 +557,29 @@ struct thread *thread__main_thread(struct machine *machine, struct thread *threareturn machine__find_thread (machine, thread->pid_, thread->pid_); } + +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 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
@@ -101,14 +101,25 @@ struct thread *thread__main_thread(struct machine *machine, struct thread *threavoid 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.13.6
Jiri Olsa
reviewerPATCH 23/49 perf callchain: Use thread__find_addr_location_by_time() and friends
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
Find correct thread/map/symbol using proper functions.
--- tools/perf/util/machine.c | 22 +++++++++++++--------- tools/perf/util/unwind-libdw.c | 12 +++++++----- tools/perf/util/unwind-libunwind-local.c | 16 ++++++++-------- 3 files changed, 28 insertions(+), 22 deletions(-)
tools/perf/util/machine.c +13 -9
@@ -1950,7 +1950,8 @@ static int add_callchain_ip(struct thread *thread,bool branch, struct branch_flags *flags, struct iterations *iter, - u64 branch_from) + u64 branch_from, + u64 timestamp) { struct addr_location al;int nr_loop_iter =0 ;
@@ -1960,8 +1961,9 @@ static int add_callchain_ip(struct thread *thread, 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) {
@@ -1986,8 +1988,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 ) {
@@ -2184,7 +2187,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, err =add_callchain_ip (thread, cursor, parent, root_al, &cpumode, ip, branch, flags,NULL , - branch_from); + branch_from, sample->time);if (err)return (err <0 ) ? err :0 ; }
@@ -2207,6 +2210,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,int chain_nr =0 ;u8 cpumode =PERF_RECORD_MISC_USER ;int i, j, err, nr_entries; +u64 timestamp = sample->time;int skip_idx =-1 ;int first_call =0 ;
@@ -2280,13 +2284,13 @@ static int thread__resolve_callchain_sample(struct thread *thread, root_al,NULL , be[i].to, true, &be[i].flags, - NULL, be[i].from); + NULL, be[i].from, timestamp);if (!err) err =add_callchain_ip (thread, cursor, parent, root_al,NULL , be[i].from, true, &be[i].flags, - &iter[i],0 ); + &iter[i],0 , timestamp);if (err == -EINVAL )break ;if (err)
@@ -2320,7 +2324,7 @@ static int thread__resolve_callchain_sample(struct thread *thread, err =add_callchain_ip (thread, cursor, parent, root_al, &cpumode, ip, - false,NULL ,NULL ,0 ); + false,NULL ,NULL ,0 , timestamp);if (err)return (err <0 ) ? err :0 ;
tools/perf/util/unwind-libdw.c +7 -5
@@ -29,9 +29,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;
@@ -103,8 +103,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) {/* * We've seen cases (softice) where DWARF unwinder went
tools/perf/util/unwind-libunwind-local.c +8 -8
@@ -367,16 +367,16 @@ 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);if (!al.map) {/* * We've seen cases (softice) where DWARF unwinder went * through non executable mmaps, which we need to lookup * in MAP__VARIABLE tree. */ -thread__find_addr_map (ui->thread,PERF_RECORD_MISC_USER , -MAP__VARIABLE , ip, &al); +thread__find_addr_map_by_time (ui->thread,PERF_RECORD_MISC_USER , +MAP__VARIABLE , ip, &al, ui->sample->time); }return al.map; }
@@ -580,14 +580,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 = al.addr; e.map = al.map;
@@ -718,7 +718,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,if (callchain_param.order ==ORDER_CALLER ) j = max_stack - i -1 ; - ret = ips[j] ?entry (ips[j], ui->thread, cb, arg) :0 ; + ret = ips[j] ?entry (ips[j], ui->thread, ui->sample->time, cb, arg) :0 ; }return ret;
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 24/49 perf tools: Add a test case for timed map groups handling
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
A 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 | 94 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 tools/perf/tests/thread-mg-time.c
tools/perf/tests/Build +1 -0
@@ -26,6 +26,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
@@ -279,6 +279,10 @@ static struct test generic_tests[] = { .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
@@ -104,6 +104,7 @@ int test__clang_subtest_get_nr(void);int test__unit_number__scnprint (struct test *test,int subtest);int test__thread_comm (struct test *test,int subtest);int test__thread_lookup_time (struct test *test,int subtest); +int test__thread_mg_time (struct test *test,int subtest);bool test__bp_signal_is_supported (void );
tools/perf/tests/thread-mg-time.c new file +94 -0
94 lines not on this page · raw message
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 25/49 perf tools: Save timestamp of a map creation
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
It'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 | 25 +++++++++++++++---------- tools/perf/util/machine.h | 2 +- tools/perf/util/map.c | 12 +++++++----- tools/perf/util/map.h | 9 ++++++--- tools/perf/util/symbol-elf.c | 2 +- tools/perf/util/symbol.c | 4 ++-- 7 files changed, 33 insertions(+), 23 deletions(-)
tools/perf/util/dso.c +1 -1
@@ -1014,7 +1014,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 +15 -10
@@ -807,7 +807,7 @@ static void dso__adjust_kmod_long_name(struct dso *dso, const char *filename) }struct map *machine__findnew_module_map (struct machine *machine,u64 start, -const char *filename) +const char *filename,u64 timestamp) {struct map *map =NULL ;struct dso *dso =NULL ;
@@ -832,7 +832,7 @@ struct map *machine__findnew_module_map(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;
@@ -1018,7 +1018,7 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)struct kmap *kmap;struct map *map; - 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 ;
@@ -1333,7 +1333,7 @@ static int machine__create_module(void *arg, const char *name, u64 start,if (arch__fix_module_text_start (&start, name) <0 )return -1 ; - map =machine__findnew_module_map (machine, start, name); + map =machine__findnew_module_map (machine, start, name,0 );if (map ==NULL )return -1 ; map->end = start + size;
@@ -1440,7 +1440,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 ];
@@ -1463,7 +1464,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine,if (event->mmap.filename[0 ] =='/' || (!is_kernel_mmap && event->mmap.filename[0 ] =='[' )) { map =machine__findnew_module_map (machine, event->mmap.start, - event->mmap.filename); + event->mmap.filename, + timestamp);if (map ==NULL ) goto out_problem;
@@ -1563,7 +1565,8 @@ int machine__process_mmap2_event(struct machine *machine,if (sample->cpumode ==PERF_RECORD_MISC_GUEST_KERNEL || sample->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 ;
@@ -1586,7 +1589,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;
@@ -1621,7 +1625,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *eventif (sample->cpumode ==PERF_RECORD_MISC_GUEST_KERNEL || sample->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 ;
@@ -1641,7 +1646,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event event->mmap.len, event->mmap.pgoff,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
@@ -229,7 +229,7 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *machine, }struct map *machine__findnew_module_map (struct machine *machine,u64 start, -const char *filename); +const char *filename,u64 timestamp);int arch__fix_module_text_start (u64 *start,const char *name); int __machine__load_kallsyms(struct machine *machine, const char *filename,
tools/perf/util/map.c +7 -5
@@ -130,7 +130,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;
@@ -144,12 +144,13 @@ void map__init(struct map *map, enum map_type type, map->groups =NULL ; map->erange_warned = false;refcount_set (&map->refcnt,1 ); + map->timestamp = timestamp; }struct map *map__new (struct machine *machine,u64 start,u64 len,u64 pgoff,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));struct nsinfo *nsi =NULL ;
@@ -203,7 +204,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;
@@ -231,7 +232,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 )));
@@ -239,7 +241,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
@@ -45,6 +45,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 );
@@ -143,12 +144,14 @@ struct thread;__map__for_each_symbol_by_name (map, sym_name, (pos)) 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 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);
tools/perf/util/symbol-elf.c +1 -1
@@ -1056,7 +1056,7 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, 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);dso__put (curr_dso);if (curr_map ==NULL ) { goto out_elf_end;
tools/perf/util/symbol.c +2 -2
@@ -843,7 +843,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__put (ndso);return -1 ;
@@ -1147,7 +1147,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.13.6
Jiri Olsa
reviewerPATCH 26/49 perf tools: Introduce map_groups__{insert,find}_by_time()
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
It'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 | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/map.h | 25 ++++++++++++++++++++ 2 files changed, 89 insertions(+)
tools/perf/util/map.c +64 -0
@@ -826,6 +826,41 @@ void maps__insert(struct maps *maps, struct map *map)up_write (&maps->lock); } +static void __maps__insert_by_time (struct maps *maps,struct map *map) +{ +struct rb_node **p = &maps->entries.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->entries); +map__get (map); +} + +void maps__insert_by_time (struct maps *maps,struct map *map) +{ +down_write (&maps->lock); +__maps__insert_by_time (maps, map); +up_write (&maps->lock); +} +static void __maps__remove (struct maps *maps,struct map *map) {rb_erase_init (&map->rb_node, &maps->entries);
@@ -864,6 +899,35 @@ struct map *maps__find(struct maps *maps, u64 ip)return m; } +struct map *maps__find_by_time (struct maps *maps,u64 ip,u64 timestamp) +{ +struct rb_node **p; +struct rb_node *parent =NULL ; +struct map *m; +struct map *best =NULL ; + +down_read (&maps->lock); + + p = &maps->entries.rb_node; +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; + } + +up_read (&maps->lock); +return best; +} +struct map *maps__first (struct maps *maps) {struct rb_node *first =rb_first (&maps->entries);
tools/perf/util/map.h +25 -0
@@ -12,6 +12,8 @@#include <linux/types.h> #include "rwsem.h" +#include "perf.h" /* for perf_has_index */ +enum map_type {MAP__FUNCTION =0 ,MAP__VARIABLE ,
@@ -189,8 +191,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 maps *maps,struct map *map); +void maps__insert_by_time (struct maps *maps,struct map *map);void maps__remove (struct maps *maps,struct map *map);struct map *maps__find (struct maps *maps,u64 addr); +struct map *maps__find_by_time (struct maps *maps,u64 addr,u64 timestamp);struct map *maps__first (struct maps *maps);struct map *map__next (struct map *map); struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
@@ -210,6 +214,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);
@@ -221,6 +236,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.13.6
Jiri Olsa
reviewerPATCH 27/49 perf tools: Use map_groups__find_addr_by_time()
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
Use 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
@@ -1491,12 +1491,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;
@@ -1505,21 +1504,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 {
@@ -1535,8 +1530,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 ) {
@@ -1567,6 +1581,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); + 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)
@@ -1587,7 +1648,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
@@ -449,8 +449,12 @@ int thread__insert_map(struct thread *thread, struct map *map)if (ret)return ret; -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); + }return 0 ; }
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 28/49 perf tools: Add testcase for managing maps with time
In reply to Jiri OlsaFrom: Namhyung Kim <[email protected]>
This 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 | 91 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 tools/perf/tests/thread-map-time.c
tools/perf/tests/Build +1 -0
@@ -27,6 +27,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
@@ -283,6 +283,10 @@ static struct test generic_tests[] = { .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
@@ -105,6 +105,7 @@ int test__unit_number__scnprint(struct test *test, int subtest);int test__thread_comm (struct test *test,int subtest);int test__thread_lookup_time (struct test *test,int subtest);int test__thread_mg_time (struct test *test,int subtest); +int test__thread_map_lookup_time (struct test *test,int subtest);bool test__bp_signal_is_supported (void );
tools/perf/tests/thread-map-time.c new file +91 -0
91 lines not on this page · raw message
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 29/49 perf callchain: Maintain libunwind's address space in map_groups
In reply to Jiri OlsaCurrently the address_space was kept in thread struct but it's more appropriate to keep it in map_groups as it's maintained throughout exec's with timestamps. Also we should not flush the address space after exec since it still can be accessed when used with an indexed data file.
--- tools/perf/util/map.h | 5 ++++- tools/perf/util/thread.h | 1 - tools/perf/util/unwind-libunwind-local.c | 25 +++++++++++++------------ tools/perf/util/unwind-libunwind.c | 9 +++++---- tools/perf/util/unwind.h | 7 ++++--- 5 files changed, 26 insertions(+), 21 deletions(-)
tools/perf/util/map.h +4 -1
@@ -71,10 +71,13 @@ struct maps {struct map_groups {struct maps maps[MAP__NR_TYPES ]; -struct machine *machine; +struct machine *machine;refcount_t refcnt;u64 timestamp;struct list_head list; +#ifdef HAVE_LIBUNWIND_SUPPORT +void *addr_space; +#endif };struct map_groups *map_groups__new (struct machine *machine);
tools/perf/util/thread.h +0 -1
@@ -41,7 +41,6 @@ struct thread {struct thread_stack *ts;struct nsinfo *nsinfo;#ifdef HAVE_LIBUNWIND_SUPPORT -void *addr_space;struct unwind_libunwind_ops *unwind_libunwind_ops;#endif };
tools/perf/util/unwind-libunwind-local.c +13 -12
@@ -629,35 +629,35 @@ static unw_accessors_t accessors = { .get_proc_name = get_proc_name, }; -static int _unwind__prepare_access (struct thread *thread) +static int _unwind__prepare_access (struct map_groups *mg) {if (callchain_param.record_mode !=CALLCHAIN_DWARF )return 0 ; - thread->addr_space =unw_create_addr_space (&accessors,0 ); -if (!thread->addr_space) { + mg->addr_space =unw_create_addr_space (&accessors,0 ); +if (!mg->addr_space) {pr_err ("unwind: Can't create unwind address space.\n" );return -ENOMEM ; } -unw_set_caching_policy (thread->addr_space,UNW_CACHE_GLOBAL ); +unw_set_caching_policy (mg->addr_space,UNW_CACHE_GLOBAL );return 0 ; } -static void _unwind__flush_access (struct thread *thread) +static void _unwind__flush_access (struct map_groups *mg) {if (callchain_param.record_mode !=CALLCHAIN_DWARF )return ; -unw_flush_cache (thread->addr_space,0 ,0 ); +unw_flush_cache (mg->addr_space,0 ,0 ); } -static void _unwind__finish_access (struct thread *thread) +static void _unwind__finish_access (struct map_groups *mg) {if (callchain_param.record_mode !=CALLCHAIN_DWARF )return ; -unw_destroy_addr_space (thread->addr_space); +unw_destroy_addr_space (mg->addr_space); } static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
@@ -665,7 +665,6 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, {u64 val;unw_word_t ips[max_stack]; -unw_addr_space_t addr_space;unw_cursor_t c;int ret, i =0 ;
@@ -681,13 +680,15 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,* unwind itself. */ if (max_stack -1 >0 ) { +struct map_groups *mg; +WARN_ONCE (!ui->thread,"WARNING: ui->thread is NULL" ); - addr_space = ui->thread->addr_space; -if (addr_space ==NULL ) + mg =thread__get_map_groups (ui->thread, ui->sample->time); +if (mg ==NULL || mg->addr_space ==NULL )return -1 ; - ret =unw_init_remote (&c, addr_space, ui); + ret =unw_init_remote (&c, mg->addr_space, ui);if (ret)display_error (ret);
tools/perf/util/unwind-libunwind.c +5 -4
@@ -18,12 +18,13 @@ static void unwind__register_ops(struct thread *thread,int unwind__prepare_access (struct thread *thread,struct map *map,bool *initialized) { +struct map_groups *mg = thread->mg;const char *arch;enum dso_type dso_type;struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;int err; -if (thread->addr_space) { +if (mg->addr_space) {pr_debug ("unwind: thread map already set, dso=%s\n" , map->dso->name);if (initialized)
@@ -56,7 +57,7 @@ int unwind__prepare_access(struct thread *thread, struct map *map, out_register:unwind__register_ops (thread, ops); - err = thread->unwind_libunwind_ops->prepare_access (thread); + err = thread->unwind_libunwind_ops->prepare_access (thread->mg);if (initialized) *initialized = err ? false : true;return err;
@@ -65,13 +66,13 @@ int unwind__prepare_access(struct thread *thread, struct map *map,void unwind__flush_access (struct thread *thread) {if (thread->unwind_libunwind_ops) - thread->unwind_libunwind_ops->flush_access (thread); + thread->unwind_libunwind_ops->flush_access (thread->mg); }void unwind__finish_access (struct thread *thread) {if (thread->unwind_libunwind_ops) - thread->unwind_libunwind_ops->finish_access (thread); + thread->unwind_libunwind_ops->finish_access (thread->mg); } int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
tools/perf/util/unwind.h +4 -3
@@ -9,6 +9,7 @@ struct map;struct perf_sample ;struct symbol ;struct thread ; +struct map_groups ;struct unwind_entry {struct map *map;
@@ -19,9 +20,9 @@ struct unwind_entry {typedef int (*unwind_entry_cb_t )(struct unwind_entry *entry,void *arg);struct unwind_libunwind_ops { -int (*prepare_access)(struct thread *thread); -void (*flush_access)(struct thread *thread); -void (*finish_access)(struct thread *thread); +int (*prepare_access)(struct map_groups *mg); +void (*flush_access)(struct map_groups *mg); +void (*finish_access)(struct map_groups *mg);int (*get_entries)(unwind_entry_cb_t cb,void *arg,struct thread *thread,struct perf_sample *data,int max_stack);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 30/49 perf tools: Rename perf_evlist__munmap_filtered to perf_mmap__put_filtered
In reply to Jiri OlsaWe will use it outside the evlist scope.
--- tools/perf/util/evlist.c | 6 +++--- tools/perf/util/evlist.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-)
tools/perf/util/evlist.c +3 -3
@@ -502,8 +502,8 @@ int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)return __perf_evlist__add_pollfd (evlist, fd,NULL ,POLLIN ); } -static void perf_evlist__munmap_filtered (struct fdarray *fda,int fd, -void *arg__maybe_unused ) +void perf_mmap__put_filtered (struct fdarray *fda,int fd, +void *arg__maybe_unused ) {struct perf_mmap *map = fda->priv[fd].ptr;
@@ -514,7 +514,7 @@ static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd,int perf_evlist__filter_pollfd (struct perf_evlist *evlist,short revents_and_mask) {return fdarray__filter (&evlist->pollfd, revents_and_mask, - perf_evlist__munmap_filtered,NULL ); + perf_mmap__put_filtered,NULL ); }int perf_evlist__poll (struct perf_evlist *evlist,int timeout)
tools/perf/util/evlist.h +1 -0
@@ -322,4 +322,5 @@ struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist, union perf_event *event);bool perf_evlist__exclude_kernel (struct perf_evlist *evlist); +void perf_mmap__put_filtered (struct fdarray *fda,int fd,void *arg);#endif /* __PERF_EVLIST_H */
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 31/49 tools lib fd array: Introduce fdarray__add_clone function
In reply to Jiri OlsaAdding fdarray__add_clone to be able to copy/clone a specific entry from fdarray struct.
It will be useful when separating event maps for
specific threads.
--- tools/lib/api/fd/array.c | 17 +++++++++++++++++ tools/lib/api/fd/array.h | 1 + 2 files changed, 18 insertions(+)
tools/lib/api/fd/array.c +17 -0
@@ -84,6 +84,23 @@ int fdarray__add(struct fdarray *fda, int fd, short revents)return pos; } +int fdarray__add_clone (struct fdarray *fda,int pos,struct fdarray *base) +{ +struct pollfd *entry; +int npos; + +if (pos >= base->nr) +return -EINVAL ; + + entry = &base->entries[pos]; + + npos =fdarray__add (fda, entry->fd, entry->events); +if (npos >=0 ) + fda->priv[npos] = base->priv[pos]; + +return npos; +} +int fdarray__filter (struct fdarray *fda,short revents,void (*entry_destructor)(struct fdarray *fda,int fd,void *arg),void *arg)
tools/lib/api/fd/array.h +1 -0
@@ -34,6 +34,7 @@ struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);void fdarray__delete (struct fdarray *fda);int fdarray__add (struct fdarray *fda,int fd,short revents); +int fdarray__add_clone (struct fdarray *fda,int pos,struct fdarray *base);int fdarray__poll (struct fdarray *fda,int timeout); int fdarray__filter(struct fdarray *fda, short revents, void (*entry_destructor)(struct fdarray *fda, int fd, void *arg),
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 32/49 tools lib subcmd: Add OPT_INTEGER_OPTARG|_SET options
In reply to Jiri OlsaAdding OPT_INTEGER_OPTARG|_SET option macros to allow optional argument with set flag.
--- tools/lib/subcmd/parse-options.c | 2 ++ tools/lib/subcmd/parse-options.h | 9 +++++++++ 2 files changed, 11 insertions(+)
tools/lib/subcmd/parse-options.c +2 -0
@@ -250,6 +250,8 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value =0 ;return 0 ; } +if (opt->set) + *(bool *)opt->set = true;if (opt->flags &PARSE_OPT_OPTARG && !p->opt) { *(int *)opt->value = opt->defval;return 0 ;
tools/lib/subcmd/parse-options.h +9 -0
@@ -144,6 +144,15 @@ struct option { .value = check_vtype(v, const char **), .argh = (a), .help = (h), \ .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d), \ .set = check_vtype(os, bool *)} +#define OPT_INTEGER_OPTARG(s, l, v, a, h, d) \ + { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), \ + .value = check_vtype(v, int *), .argh = (a), .help = (h), \ + .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) } +#define OPT_INTEGER_OPTARG_SET(s, l, v, os, a, h, d) \ + { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), \ + .value = check_vtype(v, int *), .argh = (a), .help = (h), \ + .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d), \ + .set = check_vtype(os, bool *)} #define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), .argh = (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} #define OPT_DATE(s, l, v, h) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 33/49 perf tools: Move __perf_session__process_events args into struct
In reply to Jiri OlsaWe will add yet another argument in here, so put all of them to struct to make it easy to read.
--- tools/perf/util/session.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-)
tools/perf/util/session.c +25 -15
@@ -1796,6 +1796,12 @@ fetch_mmaped_event(struct perf_session *session,return event; } +struct process_args { +u64 data_offset; +u64 data_size; +u64 file_size; +}; +/* * On 64bit we can mmap the data file in one go. No need for tiny mmap * slices. On 32bit we use 32MB.
@@ -1809,13 +1815,13 @@ fetch_mmaped_event(struct perf_session *session,#endif static int __perf_session__process_events (struct perf_session *session, -u64 data_offset,u64 data_size, -u64 file_size) +struct process_args *args) {struct ordered_events *oe = &session->ordered_events;struct perf_tool *tool = session->tool;int fd =perf_data__fd (session->data);u64 head, page_offset, file_offset, file_pos, size; +u64 file_size = args->file_size;int err, mmap_prot, mmap_flags, map_idx =0 ;size_t mmap_size;char *buf, *mmaps[NUM_MMAPS ];
@@ -1825,15 +1831,15 @@ static int __perf_session__process_events(struct perf_session *session,perf_tool__fill_defaults (tool); - page_offset = page_size * (data_offset / page_size); + page_offset = page_size * (args->data_offset / page_size); file_offset = page_offset; - head = data_offset - page_offset; + head = args->data_offset - page_offset; -if (data_size ==0 ) +if (args->data_size ==0 ) goto out; -if (data_offset + data_size < file_size) - file_size = data_offset + data_size; +if (args->data_offset + args->data_size < file_size) + file_size = args->data_offset + args->data_size;ui_progress__init_size (&prog, file_size,"Processing events..." );
@@ -1932,6 +1938,7 @@ static int __perf_session__process_events(struct perf_session *session,static int __perf_session__process_indexed_events (struct perf_session *session) { +struct process_args args;struct perf_tool *tool = session->tool;int err =0 , i;
@@ -1941,9 +1948,11 @@ static int __perf_session__process_indexed_events(struct perf_session *session)if (!idx->size)continue ; - err =__perf_session__process_events (session, idx->offset, - idx->size, - idx->offset + idx->size); + args.data_offset = idx->offset; + args.data_size = idx->size; + args.file_size = idx->offset + idx->size; + + err =__perf_session__process_events (session, &args);if (err <0 )break ; }
@@ -1956,9 +1965,9 @@ static int __perf_session__process_indexed_events(struct perf_session *session)int perf_session__process_events (struct perf_session *session) { +struct process_args args;struct perf_tool *tool = session->tool;struct perf_data *data = session->data; -u64 size =perf_data__size (data);int err;if (perf_session__register_idle_thread (session) <0 )
@@ -1969,10 +1978,11 @@ 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, - session->header.data_offset, - session->header.data_size, - size); + args.data_offset = session->header.data_offset; + args.data_size = session->header.data_size; + args.file_size =perf_data__size (data); + + err =__perf_session__process_events (session, &args);if (!tool->no_warn)perf_session__warn_about_errors (session);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 34/49 perf ui progress: Fix index progress display
In reply to Jiri OlsaDisplay overall index files progress size instead of having multiple (per index) progress bars.
--- tools/perf/util/session.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-)
tools/perf/util/session.c +23 -4
@@ -1797,6 +1797,8 @@ fetch_mmaped_event(struct perf_session *session, }struct process_args { +struct ui_progress prog; +u64 data_offset;u64 data_size;u64 file_size;
@@ -1826,7 +1828,6 @@ static int __perf_session__process_events(struct perf_session *session,size_t mmap_size;char *buf, *mmaps[NUM_MMAPS ];union perf_event *event; -struct ui_progress prog;s64 skip;perf_tool__fill_defaults (tool);
@@ -1841,8 +1842,6 @@ static int __perf_session__process_events(struct perf_session *session,if (args->data_offset + args->data_size < file_size) file_size = args->data_offset + args->data_size; -ui_progress__init_size (&prog, file_size,"Processing events..." ); - mmap_size =MMAP_SIZE ;if (mmap_size > file_size) { mmap_size = file_size;
@@ -1907,7 +1906,7 @@ static int __perf_session__process_events(struct perf_session *session, head += size; file_pos += size; -ui_progress__update (&prog, size); +ui_progress__update (&args->prog, size);if (session_done ()) goto out;
@@ -1936,12 +1935,29 @@ static int __perf_session__process_events(struct perf_session *session,return err; } +static u64 get_index_size (struct perf_session *session) +{ +u64 size =0 ; +int i; + +for (i =0 ; i < (int )session->header.nr_index; i++) { +struct perf_file_section *idx = &session->header.index[i]; + + size += idx->size; + } + +return size; +} +static int __perf_session__process_indexed_events (struct perf_session *session) {struct process_args args;struct perf_tool *tool = session->tool;int err =0 , i; +ui_progress__init_size (&args.prog,get_index_size (session), +"Processing events" ); +for (i =0 ; i < (int )session->header.nr_index; i++) {struct perf_file_section *idx = &session->header.index[i];
@@ -1982,6 +1998,9 @@ int perf_session__process_events(struct perf_session *session) args.data_size = session->header.data_size; args.file_size =perf_data__size (data); +ui_progress__init_size (&args.prog, args.file_size, +"Processing events" ); + err =__perf_session__process_events (session, &args);if (!tool->no_warn)
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 35/49 perf tools: Add threads debug variable
In reply to Jiri OlsaTo separate thread debug messages from global verbose, to enable type:
$ perf --debug threads=X record ...
where X is the debug level.
--- tools/perf/Documentation/perf.txt | 1 + tools/perf/util/debug.c | 2 ++ tools/perf/util/debug.h | 1 + 3 files changed, 4 insertions(+)
tools/perf/Documentation/perf.txt +1 -0
@@ -22,6 +22,7 @@ OPTIONS verbose - general debug messages ordered-events - ordered events object debug messages data-convert - data convert command debug messages + threads - threads debug/stats messages --buildid-dir:: Setup buildid cache directory. It has higher priority than
tools/perf/util/debug.c +2 -0
@@ -28,6 +28,7 @@ bool dump_trace = false, quiet = false;int debug_ordered_events;static int redirect_to_stderr;int debug_data_convert; +int debug_threads;int veprintf (int level,int var,const char *fmt,va_list args) {
@@ -180,6 +181,7 @@ static struct debug_variable { { .name = "ordered-events", .ptr = &debug_ordered_events}, { .name = "stderr", .ptr = &redirect_to_stderr}, { .name = "data-convert", .ptr = &debug_data_convert }, + { .name = "threads", .ptr = &debug_threads }, { .name = NULL, } };
tools/perf/util/debug.h +1 -0
@@ -15,6 +15,7 @@ extern int verbose;extern bool quiet, dump_trace;extern int debug_ordered_events;extern int debug_data_convert; +extern int debug_threads;#ifndef pr_fmt#define pr_fmt (fmt) fmt
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 36/49 perf tools: Add cpu into struct perf_mmap
In reply to Jiri OlsaIt will be useful in following patch to pin thread to cpu based on the map it processes.
--- tools/perf/util/evlist.c | 2 +- tools/perf/util/mmap.c | 3 ++- tools/perf/util/mmap.h | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-)
tools/perf/util/evlist.c +1 -1
@@ -888,7 +888,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,if (*output ==-1 ) { *output = fd; -if (perf_mmap__mmap (&maps[idx], mp, *output) <0 ) +if (perf_mmap__mmap (&maps[idx], mp, *output, cpu) <0 )return -1 ; }else {if (ioctl (fd,PERF_EVENT_IOC_SET_OUTPUT , *output) !=0 )
tools/perf/util/mmap.c +2 -1
@@ -199,7 +199,7 @@ void perf_mmap__munmap(struct perf_mmap *map)auxtrace_mmap__munmap (&map->auxtrace_mmap); } -int perf_mmap__mmap (struct perf_mmap *map,struct mmap_params *mp,int fd) +int perf_mmap__mmap (struct perf_mmap *map,struct mmap_params *mp,int fd,int cpu) {/* * The last one will be done at perf_evlist__mmap_consume(), so that we
@@ -226,6 +226,7 @@ int perf_mmap__mmap(struct perf_mmap *map, struct mmap_params *mp, int fd)return -1 ; } map->fd = fd; + map->cpu = cpu;if (auxtrace_mmap__mmap (&map->auxtrace_mmap, &mp->auxtrace_mp, map->base, fd))
tools/perf/util/mmap.h +2 -1
@@ -18,6 +18,7 @@ struct perf_mmap {void *base;int mask;int fd; +int cpu;refcount_t refcnt;u64 prev;struct auxtrace_mmap auxtrace_mmap;
@@ -59,7 +60,7 @@ struct mmap_params {bool track_mmap; }; -int perf_mmap__mmap (struct perf_mmap *map,struct mmap_params *mp,int fd); +int perf_mmap__mmap (struct perf_mmap *map,struct mmap_params *mp,int fd,int cpu);void perf_mmap__munmap (struct perf_mmap *map);void perf_mmap__get (struct perf_mmap *map);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 37/49 perf tools: Add perf_mmap__read_tail function
In reply to Jiri OlsaIt will be used in following patches.
--- tools/perf/util/mmap.h | 7 +++++++ 1 file changed, 7 insertions(+)
tools/perf/util/mmap.h +7 -0
@@ -78,6 +78,13 @@ static inline u64 perf_mmap__read_head(struct perf_mmap *mm)return head; } +static inline u64 perf_mmap__read_tail (struct perf_mmap *md) +{ +struct perf_event_mmap_page *pc = md->base; + +return pc->data_tail; +} +static inline void perf_mmap__write_tail (struct perf_mmap *md,u64 tail) {struct perf_event_mmap_page *pc = md->base;
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 38/49 perf record: Introduce struct record_thread
In reply to Jiri OlsaAdding struct record_thread to carry the single thread's maps.
--- tools/perf/builtin-record.c | 179 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+)
tools/perf/builtin-record.c +179 -0
@@ -64,6 +64,15 @@ struct switch_output {bool set; }; +struct record_thread { +struct perf_mmap **mmap; +int mmap_nr; +struct perf_mmap **ovw_mmap; +int ovw_mmap_nr; +struct fdarray pollfd; +struct record *rec; +}; +struct record {struct perf_tool tool;struct record_opts opts;
@@ -83,6 +92,8 @@ struct record {bool timestamp_boundary;struct switch_output switch_output;unsigned long long samples; +struct record_thread *threads; +int threads_cnt; };static volatile int auxtrace_record__snapshot_started;
@@ -940,6 +951,166 @@ static int record__synthesize(struct record *rec, bool tail)return err; } +static void +record_thread__clean (struct record_thread *th) +{ +free (th->mmap); +free (th->ovw_mmap); +} + +static void +record__threads_clean (struct record *rec) +{ +struct record_thread *threads = rec->threads; +int i; + +if (threads) { +for (i =0 ; i < rec->threads_cnt; i++) +record_thread__clean (threads + i); + } +} + +static void record_thread__init (struct record_thread *th,struct record *rec) +{ +memset (th,0 ,sizeof (*th)); +fdarray__init (&th->pollfd,64 ); + th->rec = rec; +} + +static int +record_thread__mmap (struct record_thread *th,int nr,int nr_ovw) +{ +struct perf_mmap **mmap; + + mmap =zalloc (sizeof (*mmap) * nr); +if (!mmap) +return -ENOMEM ; + + th->mmap = mmap; + th->mmap_nr = nr; + +if (nr_ovw) { + mmap =zalloc (sizeof (*mmap) * nr_ovw); +if (!mmap) +return -ENOMEM ; + + th->ovw_mmap = mmap; + th->ovw_mmap_nr = nr; + } + +return 0 ; +} + +static int +record__threads_assign (struct record *rec) +{ +struct record_thread *threads = rec->threads; +struct record_thread *thread0 = threads; +struct perf_evlist *evlist = rec->evlist; +int i, j, nr, nr0, nr_ovw, nr_trk; +int ret = -ENOMEM ; + + nr = evlist->mmap ? evlist->nr_mmaps :0 ; + nr_trk = evlist->track_mmap ? evlist->nr_mmaps :0 ; + nr_ovw = evlist->overwrite_mmap ? evlist->nr_mmaps :0 ; + + nr0 = nr_trk; + nr0 += nr; + +if (record_thread__mmap (thread0, nr0, nr_ovw)) + goto out_error; + +for (i =0 ; i < nr_ovw; i++) + thread0->ovw_mmap[i] = &evlist->overwrite_mmap[i]; + +for (i =0 ; i < nr_trk; i++) + thread0->mmap[i] = &evlist->track_mmap[i]; + +for (j =0 ; i < nr0 && j < nr; i++, j++) + thread0->mmap[i] = &evlist->mmap[j]; + + ret =0 ; + +out_error: +return ret; +} + +static int +record_thread__create_poll (struct record_thread *th, +struct perf_evlist *evlist) +{ +struct fdarray *fda = &evlist->pollfd; +struct perf_mmap *mmap; +int i, j; + +for (i =0 ; i < th->mmap_nr; i++) { + mmap = th->mmap[i]; + +for (j =0 ; j < fda->nr; j++) { +if (mmap != fda->priv[j].ptr) +continue ; + +if (fdarray__add_clone (&th->pollfd, j, fda) <0 ) +return -ENOMEM ; + +break ; + } + } + +return 0 ; +} + +static int +record__threads_create_poll (struct record *rec) +{ +struct record_thread *threads = rec->threads; +int ret =0 , i; + +for (i =0 ; !ret && (i < rec->threads_cnt); i++) + ret =record_thread__create_poll (threads + i, rec->evlist); + +return ret; +} + +static int +record__threads_create (struct record *rec) +{ +struct record_thread *threads; +int i, cnt = rec->threads_cnt; + + threads =zalloc (sizeof (*threads) * cnt); +if (threads) { +for (i =0 ; i < cnt; i++) +record_thread__init (threads + i, rec); + + rec->threads = threads; + } + +return threads ?0 : -ENOMEM ; +} + +static int +record__threads_config (struct record *rec) +{ +int ret; + + ret =record__threads_create (rec); +if (ret) + goto out; + + ret =record__threads_assign (rec); +if (ret) + goto out; + + ret =record__threads_create_poll (rec); + +out: +if (ret) +record__threads_clean (rec); + +return ret; +} +static int __cmd_record (struct record *rec,int argc,const char **argv) {int err;
@@ -1007,6 +1178,11 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } +if (record__threads_config (rec)) { + err =-1 ; + goto out_child; + } +if (opts->index) { err =record__mmap_index (rec);if (err)
@@ -1284,6 +1460,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)perf_hooks__invoke_record_end (); +record__threads_clean (rec); +if (!err && !quiet) {char samples[128 ]; const char *postfix = rec->timestamp_filename ?
@@ -1623,6 +1801,7 @@ static struct record record = { .mmap2 = perf_event__process_mmap2, .ordered_events = true, }, + .threads_cnt = 1, };const char record_callchain_help[] =CALLCHAIN_RECORD_HELP
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 39/49 perf record: Read record thread's mmaps
In reply to Jiri OlsaSwitch the maps source from evlist into thread data.
--- tools/perf/builtin-record.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-)
tools/perf/builtin-record.c +20 -17
@@ -96,6 +96,8 @@ struct record {int threads_cnt; }; +static __threadstruct record_thread *thread; +static volatile int auxtrace_record__snapshot_started;static DEFINE_TRIGGER (auxtrace_snapshot_trigger);static DEFINE_TRIGGER (switch_output_trigger);
@@ -538,24 +540,24 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli bool overwrite) {u64 bytes_written = rec->bytes_written; -int i; +int i, nr;int rc =0 ; -struct perf_mmap *maps; +struct perf_mmap **maps;if (!evlist)return 0 ; - maps = overwrite ? evlist->overwrite_mmap : evlist->mmap; + maps = overwrite ? thread->ovw_mmap : thread->mmap;if (!maps)return 0 ;if (overwrite && evlist->bkw_mmap_state !=BKW_MMAP_DATA_PENDING )return 0 ; -for (i =0 ; i < evlist->nr_mmaps; i++) { -struct perf_mmap *map = &maps[i]; -struct perf_mmap *track_map = evlist->track_mmap ? - &evlist->track_mmap[i] :NULL ; + nr = overwrite ? thread->ovw_mmap_nr : thread->mmap_nr; + +for (i =0 ; i < nr; i++) { +struct perf_mmap *map = maps[i];if (map->base) {if (perf_mmap__push (map, overwrite, rec, record__pushfn) !=0 ) {
@@ -569,21 +571,20 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli rc =-1 ; goto out; } - -if (track_map && track_map->base) { -if (perf_mmap__push (track_map, overwrite, rec, record__pushfn) !=0 ) { - rc =-1 ; - goto out; - } - } }/* * Mark the round finished in case we wrote * at least one event. */ -if (bytes_written != rec->bytes_written) - rc =record__write (rec,NULL , &finished_round_event,sizeof (finished_round_event)); +if (bytes_written != rec->bytes_written) { +/* +* All maps of the threads point to a single file, +* so we can just pick first one. +*/ + rc =record__write (rec, thread->mmap[0 ], &finished_round_event, +sizeof (finished_round_event)); + }if (overwrite)perf_evlist__toggle_bkw_mmap (evlist,BKW_MMAP_EMPTY );
@@ -1189,6 +1190,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } + thread = &rec->threads[0 ]; + err =bpf__apply_obj_config ();if (err) {char errbuf[BUFSIZ ];
@@ -1383,7 +1386,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)if (hits == rec->samples) {if (done || draining)break ; - err =perf_evlist__poll (rec->evlist,-1 ); + err =fdarray__poll (&thread->pollfd,-1 );/* * Propagate error, only if there's any. Ignore positive * number of returned events and interrupt error.
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 40/49 perf record: Move waking into struct record
In reply to Jiri OlsaWe need to keep global number of 'waking' now.
TODO: make this multiple threads safe.
--- tools/perf/builtin-record.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
tools/perf/builtin-record.c +5 -5
@@ -94,6 +94,7 @@ struct record {unsigned long long samples;struct record_thread *threads;int threads_cnt; +unsigned long waking; };static __threadstruct record_thread *thread;
@@ -1116,7 +1117,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) {int err;int status =0 ; -unsigned long waking =0 ;const bool forks = argc >0 ;struct machine *machine;struct perf_tool *tool = &rec->tool;
@@ -1368,8 +1368,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)if (!quiet)fprintf (stderr,"[ perf record: dump data: Woken up %ld times ]\n" , - waking); - waking =0 ; + rec->waking); + rec->waking =0 ; fd =record__switch_output (rec, false);if (fd <0 ) {pr_err ("Failed to switch to new file\n" );
@@ -1393,7 +1393,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)*/ if (err >0 || (err <0 && errno ==EINTR )) err =0 ; - waking++; + rec->waking++;if (perf_evlist__filter_pollfd (rec->evlist,POLLERR |POLLHUP ) ==0 ) draining = true;
@@ -1422,7 +1422,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) }if (!quiet) -fprintf (stderr,"[ perf record: Woken up %ld times to write data ]\n" , waking); +fprintf (stderr,"[ perf record: Woken up %ld times to write data ]\n" , rec->waking);if (target__none (&rec->opts.target))record__synthesize_workload (rec, true);
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 41/49 perf record: Move samples into struct record_thread
In reply to Jiri OlsaMoving samples value into struct record_thread, because we need to have this value per thread for checking if there have been new data.
--- tools/perf/builtin-record.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
tools/perf/builtin-record.c +6 -5
@@ -71,6 +71,7 @@ struct record_thread {int ovw_mmap_nr;struct fdarray pollfd;struct record *rec; +unsigned long long samples; };struct record {
@@ -159,7 +160,7 @@ static int record__pushfn(struct perf_mmap *map, void *to, void *bf, size_t size {struct record *rec = to; - rec->samples++; + thread->samples++;return record__write (rec, map, bf, size); }
@@ -243,7 +244,7 @@ static int record__auxtrace_mmap_read(struct record *rec,return ret;if (ret) - rec->samples++; + thread->samples++;return 0 ; }
@@ -260,7 +261,7 @@ static int record__auxtrace_mmap_read_snapshot(struct record *rec,return ret;if (ret) - rec->samples++; + thread->samples++;return 0 ; }
@@ -1314,7 +1315,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)trigger_ready (&switch_output_trigger);perf_hooks__invoke_record_start ();for (;;) { -unsigned long long hits = rec->samples; +unsigned long long hits = thread->samples;/* * rec->evlist->bkw_mmap_state is possible to be
@@ -1383,7 +1384,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)alarm (rec->switch_output.time); } -if (hits == rec->samples) { +if (hits == thread->samples) {if (done || draining)break ; err =fdarray__poll (&thread->pollfd,-1 );
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 42/49 perf record: Move bytes_written into struct record_thread
In reply to Jiri OlsaMoving bytes_written into struct record_thread.
--- tools/perf/builtin-record.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
tools/perf/builtin-record.c +8 -8
@@ -72,12 +72,12 @@ struct record_thread {struct fdarray pollfd;struct record *rec;unsigned long long samples; +u64 bytes_written; };struct record {struct perf_tool tool;struct record_opts opts; -u64 bytes_written;struct perf_data data;struct auxtrace_record *itr;struct perf_evlist *evlist;
@@ -114,7 +114,7 @@ static bool switch_output_size(struct record *rec) {return rec->switch_output.size &&trigger_is_ready (&switch_output_trigger) && - (rec->bytes_written >= rec->switch_output.size); + (thread->bytes_written >= rec->switch_output.size); }static bool switch_output_time (struct record *rec)
@@ -138,7 +138,7 @@ static int record__write(struct record *rec, struct perf_mmap *map,return -1 ; } - rec->bytes_written += size; + thread->bytes_written += size;if (switch_output_size (rec))trigger_hit (&switch_output_trigger);
@@ -541,7 +541,7 @@ static struct perf_event_header finished_round_event = {static int record__mmap_read_evlist (struct record *rec,struct perf_evlist *evlist,bool overwrite) { -u64 bytes_written = rec->bytes_written; +u64 bytes_written = thread->bytes_written;int i, nr;int rc =0 ;struct perf_mmap **maps;
@@ -579,7 +579,7 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli* Mark the round finished in case we wrote * at least one event. */ -if (bytes_written != rec->bytes_written) { +if (bytes_written != thread->bytes_written) {/* * All maps of the threads point to a single file, * so we can just pick first one.
@@ -678,6 +678,7 @@ static int record__merge_index(struct record *rec) goto out_close;pr_debug (" ok\n" ); + rec->session->header.data_size += idx[i].size; } session->header.index = idx;
@@ -704,7 +705,7 @@ record__finish_output(struct record *rec)if (data->is_pipe)return ; - rec->session->header.data_size += rec->bytes_written; + rec->session->header.data_size += thread->bytes_written;if (rec->opts.index)record__merge_index (rec);
@@ -770,7 +771,7 @@ record__switch_output(struct record *rec, bool at_exit) rec->session->header.data_offset, at_exit);if (fd >=0 && !at_exit) { - rec->bytes_written =0 ; + thread->bytes_written =0 ; rec->session->header.data_size =0 ; }
@@ -890,7 +891,6 @@ static int record__synthesize(struct record *rec, bool tail)pr_err ("Couldn't record tracing data.\n" ); goto out; } - rec->bytes_written += err; } }
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 43/49 perf record: Add record_thread start/stop/process functions
In reply to Jiri OlsaAdding thread process and API to start/stop it.
--- tools/perf/builtin-record.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)
tools/perf/builtin-record.c +83 -0
@@ -64,6 +64,11 @@ struct switch_output {bool set; }; +enum { +RECORD_THREAD__RUNNING =0 , +RECORD_THREAD__STOP =1 , +}; +struct record_thread {struct perf_mmap **mmap;int mmap_nr;
@@ -73,6 +78,8 @@ struct record_thread {struct record *rec;unsigned long long samples;u64 bytes_written; +pthread_t pt; +int state; };struct record {
@@ -1114,6 +1121,75 @@ record__threads_config(struct record *rec)return ret; } +static void * +record_thread__process (struct record *rec) +{ +while (thread->state !=RECORD_THREAD__STOP ) { +unsigned long long hits = thread->samples; +int err; + +if (record__mmap_read_all (thread->rec) <0 ) +break ; + +if (hits == thread->samples) { + err =fdarray__poll (&thread->pollfd,500 ); +/* +* Propagate error, only if there's any. Ignore positive +* number of returned events and interrupt error. +*/ +if (err >0 || (err <0 && errno ==EINTR )) + err =0 ; + rec->waking++; + +if (fdarray__filter (&thread->pollfd,POLLERR |POLLHUP , + perf_mmap__put_filtered,NULL ) ==0 ) +break ; + } + } + +return NULL ; +} + +static void *worker (void *arg) +{ +struct record_thread *th = arg; +struct record *rec = th->rec; + + thread = th; + thread->state =RECORD_THREAD__RUNNING ; + +return record_thread__process (rec); +} + +static int record__threads_start (struct record *rec) +{ +struct record_thread *threads = rec->threads; +int i, err =0 ; + +for (i =1 ; !err && i < rec->threads_cnt; i++) { +struct record_thread *th = threads + i; + + err =pthread_create (&th->pt,NULL , worker, th); + } + +return err; +} + +static int record__threads_stop (struct record *rec) +{ +struct record_thread *threads = rec->threads; +int i, err =0 ; + +for (i =1 ; !err && i < rec->threads_cnt; i++) { +struct record_thread *th = threads + i; + + th->state =RECORD_THREAD__STOP ; + err =pthread_join (th->pt,NULL ); + } + +return err; +} +static int __cmd_record (struct record *rec,int argc,const char **argv) {int err;
@@ -1250,6 +1326,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } } + err =record__threads_start (rec); +if (err <0 ) + goto out_child; +/* * When perf is starting the traced process, all the events * (apart from group members) have enable_on_exec=1 set,
@@ -1414,6 +1494,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)trigger_off (&auxtrace_snapshot_trigger);trigger_off (&switch_output_trigger); +if (record__threads_stop (rec)) +pr_err ("failed to stop threads\n" ); +if (forks && workload_exec_errno) {char msg[STRERR_BUFSIZE ];const char *emsg =str_error_r (workload_exec_errno, msg,sizeof (msg));
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 44/49 perf record: Wait for all threads being started
In reply to Jiri OlsaEnsure all threads are started and ready before
we enable events. Using pthread_cond_t signaling
logic for that.
--- tools/perf/builtin-record.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
tools/perf/builtin-record.c +33 -0
@@ -102,6 +102,9 @@ struct record {unsigned long long samples;struct record_thread *threads;int threads_cnt; +int threads_signal_cnt; +pthread_mutex_t threads_signal_mutex; +pthread_cond_t threads_signal_cond;unsigned long waking; };
@@ -1114,6 +1117,9 @@ record__threads_config(struct record *rec) ret =record__threads_create_poll (rec); +pthread_mutex_init (&rec->threads_signal_mutex,NULL ); +pthread_cond_init (&rec->threads_signal_cond,NULL ); + out:if (ret)record__threads_clean (rec);
@@ -1150,6 +1156,26 @@ record_thread__process(struct record *rec)return NULL ; } +static void signal_main (struct record *rec) +{ +pthread_mutex_lock (&rec->threads_signal_mutex); + rec->threads_signal_cnt++; +pthread_cond_signal (&rec->threads_signal_cond); +pthread_mutex_unlock (&rec->threads_signal_mutex); +} + +static void wait_for_signal (struct record *rec) +{ +pthread_mutex_lock (&rec->threads_signal_mutex); + +while (rec->threads_signal_cnt < rec->threads_cnt) { +pthread_cond_wait (&rec->threads_signal_cond, + &rec->threads_signal_mutex); + } + +pthread_mutex_unlock (&rec->threads_signal_mutex); +} +static void *worker (void *arg) {struct record_thread *th = arg;
@@ -1158,6 +1184,8 @@ static void *worker(void *arg) thread = th; thread->state =RECORD_THREAD__RUNNING ; +signal_main (rec); +return record_thread__process (rec); }
@@ -1166,12 +1194,17 @@ static int record__threads_start(struct record *rec)struct record_thread *threads = rec->threads;int i, err =0 ; + rec->threads_signal_cnt =1 ; +for (i =1 ; !err && i < rec->threads_cnt; i++) {struct record_thread *th = threads + i; err =pthread_create (&th->pt,NULL , worker, th); } +if (rec->threads_cnt >1 ) +wait_for_signal (rec); +return err; }
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 45/49 perf record: Add --threads option
In reply to Jiri OlsaAllows to assign number to record::threads_cnt and thus to create multiple threads. At this point we don't allow to specify number of threads, instead we assign it number of evlist's mmaps to have a single thread for each.
--- tools/perf/builtin-record.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
tools/perf/builtin-record.c +36 -0
@@ -102,6 +102,7 @@ struct record {unsigned long long samples;struct record_thread *threads;int threads_cnt; +bool threads_set;int threads_signal_cnt;pthread_mutex_t threads_signal_mutex;pthread_cond_t threads_signal_cond;
@@ -1102,11 +1103,38 @@ record__threads_create(struct record *rec)return threads ?0 : -ENOMEM ; } +static int record__threads_cnt (struct record *rec) +{ +struct perf_evlist *evlist = rec->evlist; +int cnt; + +if (rec->threads_set) { +if (rec->threads_cnt) { +pr_err ("failed: Can't specify number of threads yet.\n" ); +return -EINVAL ; + } +if (evlist->overwrite_mmap) { +pr_err ("failed: Can't use multiple threads with overwrite mmaps yet.\n" ); +return -EINVAL ; + } + cnt = evlist->nr_mmaps; + }else { + cnt =1 ; + } + + rec->threads_cnt = cnt; +return 0 ; +} +static int record__threads_config (struct record *rec) {int ret; + ret =record__threads_cnt (rec); +if (ret) + goto out; + ret =record__threads_create (rec);if (ret) goto out;
@@ -2067,6 +2095,8 @@ static struct option __record_options[] = { "Parse options then exit"),OPT_BOOLEAN (0 ,"index" , &record.opts.index,"make index for sample data to speed-up processing" ), +OPT_INTEGER_OPTARG_SET (0 ,"threads" , &record.threads_cnt, &record.threads_set, +"count" ,"Enabled threads (count)" ,0 ), OPT_END() };
@@ -2224,6 +2254,12 @@ int cmd_record(int argc, const char **argv) goto out; } +/* +* Threads need index data file. +*/ +if (record.threads_set) + record.opts.index = true; +if (rec->opts.index) {if (!rec->opts.sample_time) {pr_err ("Sample timestamp is required for indexing\n" );
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 46/49 perf record: Add --thread-stats option support
In reply to Jiri OlsaAdd per-thread stats to have an idea what's happening in the main reading loop.
$ perf --debug threads=2 record ...
SNIP
pid write poll skip
1s 8914 136B 1 0
2s 8914 512K 43 79
3s 8914 3M 214 385
4s 8914 3M 121 291 $ perf --debug threads=2 record --threads ...
SNIP
pid write poll skip
1s 9770 144B 1 0
9772 0B 1 0
9773 0B 1 0
9774 0B 1 0
2s 9770 290K 35 37
9772 272K 36 34
9773 274K 35 35
9774 304K 39 39
3s 9770 1120K 140 140
9772 1088K 138 138
9773 1120K 140 140
9774 1123K 140 140
4s 9770 1161K 146 146
9772 1121K 142 142
9773 1135K 142 142
9774 1159K 145 145--- tools/perf/builtin-record.c | 79 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-)
tools/perf/builtin-record.c +76 -3
@@ -69,7 +69,14 @@ enum { RECORD_THREAD__STOP = 1, }; +struct thread_stat { +u64 bytes_written; +u64 poll; +u64 poll_skip; +}; +struct record_thread { +int pid;struct perf_mmap **mmap;int mmap_nr;struct perf_mmap **ovw_mmap;
@@ -80,6 +87,7 @@ struct record_thread {u64 bytes_written;pthread_t pt;int state; +struct thread_stat stats; };struct record {
@@ -149,7 +157,8 @@ static int record__write(struct record *rec, struct perf_mmap *map,return -1 ; } - thread->bytes_written += size; + thread->bytes_written += size; + thread->stats.bytes_written += size;if (switch_output_size (rec))trigger_hit (&switch_output_trigger);
@@ -1155,6 +1164,11 @@ record__threads_config(struct record *rec)return ret; } +static inline pid_t gettid (void ) +{ +return (pid_t )syscall (__NR_gettid); +} +static void *record_thread__process (struct record *rec) {
@@ -1166,6 +1180,8 @@ record_thread__process(struct record *rec)break ;if (hits == thread->samples) { + thread->stats.poll++; + err =fdarray__poll (&thread->pollfd,500 );/* * Propagate error, only if there's any. Ignore positive
@@ -1178,6 +1194,8 @@ record_thread__process(struct record *rec)if (fdarray__filter (&thread->pollfd,POLLERR |POLLHUP , perf_mmap__put_filtered,NULL ) ==0 )break ; + }else { + thread->stats.poll_skip++; } }
@@ -1210,6 +1228,7 @@ static void *worker(void *arg)struct record *rec = th->rec; thread = th; + thread->pid =gettid (); thread->state =RECORD_THREAD__RUNNING ;signal_main (rec);
@@ -1251,6 +1270,50 @@ static int record__threads_stop(struct record *rec)return err; } +static void record_thread__display (struct record_thread *t,unsigned long s) +{ +char buf_size[20 ]; +char buf_time[20 ]; + +unit_number__scnprintf (buf_size,sizeof (buf_size), t->stats.bytes_written); + +if (s) +scnprintf (buf_time,sizeof (buf_time),"%5lus" , s); +else + buf_time[0 ] =0 ; + +fprintf (stderr,"%6s %6d %10s %10" PRIu64" %10" PRIu64"\n" , + buf_time, t->pid, buf_size, t->stats.poll, t->stats.poll_skip); +} + +static void record__threads_stats (struct record *rec) +{ +struct record_thread *threads = rec->threads; +static time_t last, last_header, start; +time_t current =time (NULL ); +int i; + +if (last == current) +return ; + +if (!start) + start = current -1 ; + + last = current; + +if (!last_header || (last_header +10 < current)) { +fprintf (stderr,"%6s %6s %10s %10s %10s\n" ," " ,"pid" ,"write" ,"poll" ,"skip" ); + last_header = current; + } + +for (i =0 ; i < rec->threads_cnt; i++) { +struct record_thread *t = threads + i; + +record_thread__display (t, !i ? current - start :0 ); +memset (&t->stats,0 ,sizeof (t->stats)); + } +} +static int __cmd_record (struct record *rec,int argc,const char **argv) {int err;
@@ -1329,6 +1392,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } thread = &rec->threads[0 ]; + thread->pid =gettid (); err =bpf__apply_obj_config ();if (err) {
@@ -1528,7 +1592,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)if (hits == thread->samples) {if (done || draining)break ; - err =fdarray__poll (&thread->pollfd,-1 ); + + err =fdarray__poll (&thread->pollfd,1000 ); + thread->stats.poll++; +/* * Propagate error, only if there's any. Ignore positive * number of returned events and interrupt error.
@@ -1537,10 +1604,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) err =0 ; rec->waking++; -if (perf_evlist__filter_pollfd (rec->evlist,POLLERR |POLLHUP ) ==0 ) +if (fdarray__filter (&thread->pollfd,POLLERR |POLLHUP , + perf_mmap__put_filtered,NULL ) ==0 ) draining = true; + }else { + thread->stats.poll_skip++; } +if (debug_threads) +record__threads_stats (rec); +/* * When perf is starting the traced process, at the end events * die with the process and we wait for that. Thus no need to
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 47/49 perf record: Add maps to --thread-stats output
In reply to Jiri OlsaDisplay free size of thread's memory maps as part
of --thread-stats output.
$ perf --debug threads=2 record ...
...
pid write poll skip maps (size 20K)
1s 8914 136B 1 0 19K 19K 19K 19K
2s 8914 512K 43 79 19K 19K 17K 19K
3s 8914 3M 214 385 17K 16K 16K 17K
4s 8914 3M 121 291 17K 17K 18K 18K
...--- tools/perf/builtin-record.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-)
tools/perf/builtin-record.c +33 -4
@@ -1270,10 +1270,25 @@ static int record__threads_stop(struct record *rec)return err; } -static void record_thread__display (struct record_thread *t,unsigned long s) +/* Stolen from kernel. */ +#define CIRC_CNT (head, tail, size) (((head) - (tail)) & ((size) - 1)) +#define CIRC_SPACE (head, tail, size) CIRC_CNT((tail), ((head)+1), (size)) + +static u64 mmap_free_size (struct perf_mmap *map,struct perf_evlist *evlist) +{ +u64 head =perf_mmap__read_head (map); +u64 tail =perf_mmap__read_tail (map); + +return CIRC_SPACE (head, tail, evlist->mmap_len); +} + +static void +record_thread__display (struct record_thread *t,struct perf_evlist *evlist, +unsigned long s) {char buf_size[20 ];char buf_time[20 ]; +int i;unit_number__scnprintf (buf_size,sizeof (buf_size), t->stats.bytes_written);
@@ -1282,15 +1297,26 @@ static void record_thread__display(struct record_thread *t, unsigned long s)else buf_time[0 ] =0 ; -fprintf (stderr,"%6s %6d %10s %10" PRIu64" %10" PRIu64"\n" , +fprintf (stderr,"%6s %6d %10s %10" PRIu64" %10" PRIu64" " , buf_time, t->pid, buf_size, t->stats.poll, t->stats.poll_skip); + +for (i =0 ; i < t->mmap_nr; i++) { +u64 size =mmap_free_size (t->mmap[i], evlist); + +unit_number__scnprintf (buf_size,sizeof (buf_size), size); +fprintf (stderr,"%5s " , buf_size); + } + +fprintf (stderr,"\n" ); }static void record__threads_stats (struct record *rec) {struct record_thread *threads = rec->threads; +struct perf_evlist *evlist = rec->evlist;static time_t last, last_header, start;time_t current =time (NULL ); +char buf_size[20 ];int i;if (last == current)
@@ -1301,15 +1327,18 @@ static void record__threads_stats(struct record *rec) last = current; +unit_number__scnprintf (buf_size,sizeof (buf_size), evlist->mmap_len); +if (!last_header || (last_header +10 < current)) { -fprintf (stderr,"%6s %6s %10s %10s %10s\n" ," " ,"pid" ,"write" ,"poll" ,"skip" ); +fprintf (stderr,"%6s %6s %10s %10s %10s %5s (size %s)\n" , +" " ,"pid" ,"write" ,"poll" ,"skip" ,"maps" , buf_size); last_header = current; }for (i =0 ; i < rec->threads_cnt; i++) {struct record_thread *t = threads + i; -record_thread__display (t, !i ? current - start :0 ); +record_thread__display (t, evlist, !i ? current - start :0 );memset (&t->stats,0 ,sizeof (t->stats)); } }
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 48/49 perf record: Spread maps for --threads option
In reply to Jiri OlsaCurrently we assign all maps to main thread. Adding code that spreads maps for --threads option.
For --thread option we create as many threads as there are memory maps in evlist, which is the number of CPUs in the system or CPUs we monitor. Each thread gets a single data mmap to read.
In addition we have also same amount of tracking mmaps for auxiliary events which we don't create special thread for. Instead we assign the to the main thread, because there's not much traffic expected there.
The assignment is visible from --thread-stats output:
pid write poll skip maps (size 20K)
1s 9770 144B 1 0 19K 19K 19K 18K 19K
9772 0B 1 0 18K
9773 0B 1 0 19K
9774 0B 1 0 19K
There are 5 maps for thread 9770 (1 data map and 4 auxiliary) and one data map for every other thread. Each thread writes data to the separate data file.
In addition we also pin every thread to the cpu that the data map belongs to in order to keep both writer (kernel) and reader (perf tool thread) on the same CPU.
--- tools/perf/builtin-record.c | 133 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 8 deletions(-)
tools/perf/builtin-record.c +125 -8
@@ -109,6 +109,8 @@ struct record {struct switch_output switch_output;unsigned long long samples;struct record_thread *threads; +bool threads_all; +bool threads_one;int threads_cnt;bool threads_set;int threads_signal_cnt;
@@ -370,15 +372,11 @@ static int record__mmap_evlist(struct record *rec,return 0 ; } -static int record__mmap_index (struct record *rec) +static void record__mmap_index_single (struct record *rec) {struct perf_evlist *evlist = rec->evlist;struct perf_data *data = &rec->data; -int i, ret, nr = evlist->nr_mmaps; - - ret =perf_data__create_index (data, nr); -if (ret) -return ret; +int i, nr = evlist->nr_mmaps;for (i =0 ; i < nr; i++) {struct perf_mmap *map = &evlist->mmap[i];
@@ -391,6 +389,50 @@ static int record__mmap_index(struct record *rec) map->file = &data->file; } +} + +static void record__mmap_index_all (struct record *rec) +{ +struct perf_evlist *evlist = rec->evlist; +struct perf_data *data = &rec->data; +struct record_thread *threads = rec->threads; +struct record_thread *thread0 = threads; +int i, t; + +BUG_ON (data->index_nr != rec->threads_cnt); + +for (i =0 ; i < evlist->nr_mmaps; i++) { +struct perf_mmap *map = &evlist->track_mmap[i]; + + map->file = &data->file; + } + + thread0->mmap[0 ]->file = &data->index[0 ]; + +for (t =1 ; t < rec->threads_cnt; t++) { +struct record_thread *th = threads + t; + +for (i =0 ; i < th->mmap_nr; i++) { +struct perf_mmap *map = th->mmap[i]; + + map->file = &data->index[t]; + } + } +} + +static int record__mmap_index (struct record *rec) +{ +struct perf_data *data = &rec->data; +int ret; + + ret =perf_data__create_index (data, rec->threads_cnt); +if (ret) +return ret; + +if (rec->threads_all) +record__mmap_index_all (rec); +else if (rec->threads_one) +record__mmap_index_single (rec);return 0 ; }
@@ -1025,7 +1067,7 @@ record_thread__mmap(struct record_thread *th, int nr, int nr_ovw) }static int -record__threads_assign (struct record *rec) +record__threads_assign_single (struct record *rec) {struct record_thread *threads = rec->threads;struct record_thread *thread0 = threads;
@@ -1059,6 +1101,55 @@ record__threads_assign(struct record *rec) }static int +record__threads_assign_all (struct record *rec) +{ +struct perf_evlist *evlist = rec->evlist; +struct record_thread *threads = rec->threads; +struct record_thread *thread0 = threads; +int cnt = rec->threads_cnt; +int i, t, nr, nr0, nr_trk; +int nr_cpus =cpu__max_present_cpu (); + + nr = evlist->mmap ? evlist->nr_mmaps :0 ; + nr_trk = evlist->track_mmap ? evlist->nr_mmaps :0 ; + +BUG_ON (evlist->overwrite_mmap); +BUG_ON (nr_cpus != nr); + + nr0 =1 + nr_trk; + +if (record_thread__mmap (thread0, nr0,0 )) +return -ENOMEM ; + + thread0->mmap[0 ] = &evlist->mmap[0 ]; + +for (i =0 ; i < nr_trk; i++) + thread0->mmap[i +1 ] = &evlist->track_mmap[i]; + +for (t =1 ; t < cnt; t++) { +struct record_thread *th = threads + t; + +if (record_thread__mmap (th,1 ,0 )) +return -ENOMEM ; + + th->mmap[0 ] = &evlist->mmap[t]; + } + +return 0 ; +} + +static int +record__threads_assign (struct record *rec) +{ +if (rec->threads_all) +return record__threads_assign_all (rec); +else if (rec->threads_one) +return record__threads_assign_single (rec); +else +return -EINVAL ; +} + +static int record_thread__create_poll (struct record_thread *th,struct perf_evlist *evlist) {
@@ -1115,7 +1206,8 @@ record__threads_create(struct record *rec)static int record__threads_cnt (struct record *rec) {struct perf_evlist *evlist = rec->evlist; -int cnt; +bool all = false, one = false; +int cnt =0 ;if (rec->threads_set) {if (rec->threads_cnt) {
@@ -1127,11 +1219,15 @@ static int record__threads_cnt(struct record *rec)return -EINVAL ; } cnt = evlist->nr_mmaps; + all = true; }else { + one = true; cnt =1 ; } rec->threads_cnt = cnt; + rec->threads_all = all; + rec->threads_one = one;return 0 ; }
@@ -1169,6 +1265,25 @@ static inline pid_t gettid(void)return (pid_t )syscall (__NR_gettid); } +static int set_affinity (int cpu) +{ +cpu_set_t mask; + +CPU_ZERO (&mask); +CPU_SET (cpu, &mask); +return sched_setaffinity (0 ,sizeof (mask), &mask); +} + +static void set_thread_affinity (struct record *rec) +{ +if (rec->threads_all) { +struct perf_mmap *m0 = thread->mmap[0 ]; + +if (set_affinity (m0->cpu)) +pr_err ("failed to set affinity for cpu %d\n" , m0->cpu); + } +} +static void *record_thread__process (struct record *rec) {
@@ -1232,6 +1347,7 @@ static void *worker(void *arg) thread->state =RECORD_THREAD__RUNNING ;signal_main (rec); +set_thread_affinity (rec);return record_thread__process (rec); }
@@ -1252,6 +1368,7 @@ static int record__threads_start(struct record *rec)if (rec->threads_cnt >1 )wait_for_signal (rec); +set_thread_affinity (rec);return err; }
Signature · 1 line
2.13.6
Jiri Olsa
reviewerPATCH 49/49 perf record: Spread maps for --threads=X option
In reply to Jiri OlsaThis code allows to create arbitrary number of threads and assign maps for it.
Each thread gets a fair share of the data maps plus the main thread gets also to read tracking maps.
The assignment is visible from --thread-stats output (for --threads=2 on 4 CPUs system):
pid write poll skip maps (size 20K)
1s 9318 144B 1 0 18K 19K 17K 19K 19K 19K
9320 16K 3 3 19K 18KThere are 6 maps for thread 9318 (2 data maps and 4 auxiliary) and 2 data maps for thread 9320. Each thread writes data to the separate data file.
--- tools/perf/builtin-record.c | 103 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-)
tools/perf/builtin-record.c +98 -5
@@ -420,6 +420,33 @@ static void record__mmap_index_all(struct record *rec) } } +static void record__mmap_index_cnt (struct record *rec) +{ +struct perf_evlist *evlist = rec->evlist; +struct perf_data *data = &rec->data; +int i, t; + +for (i =0 ; i < evlist->nr_mmaps; i++) { +struct perf_mmap *map = &evlist->track_mmap[i]; + + map->file = &data->file; + } + +for (t =0 ; t < rec->threads_cnt; t++) { +struct record_thread *th = rec->threads + t; +int nr = th->mmap_nr; + +if (!t) + nr -= evlist->nr_mmaps; + +for (i =0 ; i < nr; i++) { +struct perf_mmap *map = th->mmap[i]; + + map->file = &data->index[t]; + } + } +} +static int record__mmap_index (struct record *rec) {struct perf_data *data = &rec->data;
@@ -433,6 +460,8 @@ static int record__mmap_index(struct record *rec)record__mmap_index_all (rec); else if (rec->threads_one) record__mmap_index_single(rec); + else +record__mmap_index_cnt (rec );return 0 ; }
@@ -1139,6 +1168,70 @@ record__threads_assign_all(struct record *rec) }static int +record__threads_assign_cnt (struct record *rec) +{ +struct record_thread *threads = rec->threads; +struct record_thread *thread0 = threads; +struct perf_evlist *evlist = rec->evlist; +int cnt = rec->threads_cnt; +int i, j, t, nr, nr_trk, nr_thr, nr_mod, n0 =0 ; + + nr = evlist->mmap ? evlist->nr_mmaps :0 ; + nr_trk = evlist->track_mmap ? evlist->nr_mmaps :0 ; + + nr_thr = nr / cnt; + nr_mod = nr % cnt; + +/* +* Create threads' mmaps first.. +*/ +for (t =0 ; t < cnt; t++) { +struct record_thread *th = threads + t; +int n = nr_thr; + +/* evenly spread the remainder of threads */ + n += (nr_mod-- >0 ) ?1 :0 ; + +/* first thread deals with track mmaps */ +if (!t) { + n0 = n; + n += nr_trk; + } + +if (record_thread__mmap (th, n,0 )) +return -ENOMEM ; + } + +/* +* ... and assign mmaps separatelly. +*/ + + j =0 ;/* mmap in evlist->mmap */ + i =0 ;/* mmap in thread */ + +while (1 ) { +for (t =0 ; t < cnt; t++) { +struct record_thread *th = threads + t; + +if (i == th->mmap_nr) +continue ; + + th->mmap[i] = &evlist->mmap[j++]; + +if (j == nr) + goto out; + } + i++; + } +out: +/* Assign track maps to thread 0. */ +for (i = n0, j =0 ; j < nr_trk && i < thread0->mmap_nr; i++, j++) + thread0->mmap[i] = &evlist->track_mmap[j]; + +return 0 ; +} + +static int record__threads_assign (struct record *rec) {if (rec->threads_all)
@@ -1146,7 +1239,7 @@ record__threads_assign(struct record *rec)else if (rec->threads_one)return record__threads_assign_single (rec);else -return -EINVAL ; +return record__threads_assign_cnt (rec); } static int
@@ -1211,15 +1304,15 @@ static int record__threads_cnt(struct record *rec)if (rec->threads_set) {if (rec->threads_cnt) { -pr_err ("failed: Can't specify number of threads yet.\n" ); -return -EINVAL ; + cnt = rec->threads_cnt; + }else { + cnt = evlist->nr_mmaps; + all = true; }if (evlist->overwrite_mmap) {pr_err ("failed: Can't use multiple threads with overwrite mmaps yet.\n" );return -EINVAL ; } - cnt = evlist->nr_mmaps; - all = true; }else { one = true; cnt =1 ;
Signature · 1 line
2.13.6
Quoted 5 lines · Jiri Olsa
hi,
sending *RFC* for threads support in perf record command.In big picture this patchset adds perf record --threads option that allows to create threads in following modes:
rebased to current Arnaldo's perf/core with few bugs fixed
git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf.git
perf/record_threads
jirka