diff options
author | Yabin Cui <yabinc@google.com> | 2017-08-07 23:11:34 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-08-07 23:11:34 +0000 |
commit | f9ed7b5507b4355021acc22adc14451e2e2d63ef (patch) | |
tree | 023d2c16bb9ff919ba93755adf310f745baef00f | |
parent | 6278bc9fd84a5a53c278357628bc947f2b91c3f5 (diff) | |
parent | 20b49f8991b55eda3309a0bbe3c18153376065da (diff) | |
download | extras-f9ed7b5507b4355021acc22adc14451e2e2d63ef.tar.gz |
Merge "simpleperf: allow recording events in different speed."
-rw-r--r-- | simpleperf/cmd_record.cpp | 128 | ||||
-rw-r--r-- | simpleperf/cmd_record_test.cpp | 48 | ||||
-rw-r--r-- | simpleperf/event_selection_set.cpp | 53 | ||||
-rw-r--r-- | simpleperf/event_selection_set.h | 22 |
4 files changed, 146 insertions, 105 deletions
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 493f094d..487e2199 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -78,26 +78,18 @@ class RecordCommand : public Command { " Gather sampling information of running [command]. And -a/-p/-t option\n" " can be used to change target of sampling information.\n" " The default options are: -e cpu-cycles -f 4000 -o perf.data.\n" +"Select monitored threads:\n" "-a System-wide collection.\n" #if defined(__ANDROID__) "--app package_name Profile the process of an Android application.\n" " On non-rooted devices, the app must be debuggable,\n" " because we use run-as to switch to the app's context.\n" #endif -"-b Enable take branch stack sampling. Same as '-j any'\n" -"-c count Set event sample period. It means recording one sample when\n" -" [count] events happen. Can't be used with -f/-F option.\n" -" For tracepoint events, the default option is -c 1.\n" -"--call-graph fp | dwarf[,<dump_stack_size>]\n" -" Enable call graph recording. Use frame pointer or dwarf debug\n" -" frame as the method to parse call graph in stack.\n" -" Default is dwarf,65528.\n" -"--cpu cpu_item1,cpu_item2,...\n" -" Collect samples only on the selected cpus. cpu_item can be cpu\n" -" number like 1, or cpu range like 0-3.\n" -"--duration time_in_sec Monitor for time_in_sec seconds instead of running\n" -" [command]. Here time_in_sec may be any positive\n" -" floating point number.\n" +"-p pid1,pid2,... Record events on existing processes. Mutually exclusive\n" +" with -a.\n" +"-t tid1,tid2,... Record events on existing threads. Mutually exclusive with -a.\n" +"\n" +"Select monitored event types:\n" "-e event1[:modifier1],event2[:modifier2],...\n" " Select the event list to sample. Use `simpleperf list` to find\n" " all possible event names. Modifiers can be added to define how\n" @@ -105,15 +97,34 @@ class RecordCommand : public Command { " Possible modifiers are:\n" " u - monitor user space events only\n" " k - monitor kernel space events only\n" -"-f freq Set event sample frequency. It means recording at most [freq]\n" -" samples every second. For non-tracepoint events, the default\n" -" option is -f 4000.\n" -"-F freq Same as '-f freq'.\n" -"-g Same as '--call-graph dwarf'.\n" "--group event1[:modifier],event2[:modifier2],...\n" " Similar to -e option. But events specified in the same --group\n" " option are monitored as a group, and scheduled in and out at the\n" " same time.\n" +"--trace-offcpu Generate samples when threads are scheduled off cpu.\n" +" Similar to \"-c 1 -e sched:sched_switch\".\n" +"\n" +"Select monitoring options:\n" +"-f freq Set event sample frequency. It means recording at most [freq]\n" +" samples every second. For non-tracepoint events, the default\n" +" option is -f 4000. A -f/-c option affects all event types\n" +" following it until meeting another -f/-c option. For example," +" for \"-f 1000 cpu-cycles -c 1 -e sched:sched_switch\", cpu-cycles\n" +" has sample freq 1000, sched:sched_switch event has sample period 1.\n" +"-c count Set event sample period. It means recording one sample when\n" +" [count] events happen. For tracepoint events, the default option\n" +" is -c 1.\n" +"--call-graph fp | dwarf[,<dump_stack_size>]\n" +" Enable call graph recording. Use frame pointer or dwarf debug\n" +" frame as the method to parse call graph in stack.\n" +" Default is dwarf,65528.\n" +"-g Same as '--call-graph dwarf'.\n" +"--cpu cpu_item1,cpu_item2,...\n" +" Collect samples only on the selected cpus. cpu_item can be cpu\n" +" number like 1, or cpu range like 0-3.\n" +"--duration time_in_sec Monitor for time_in_sec seconds instead of running\n" +" [command]. Here time_in_sec may be any positive\n" +" floating point number.\n" "-j branch_filter1,branch_filter2,...\n" " Enable taken branch stack sampling. Each sample captures a series\n" " of consecutive taken branches.\n" @@ -126,6 +137,7 @@ class RecordCommand : public Command { " k: only when the branch target is in the kernel\n" " This option requires at least one branch type among any, any_call,\n" " any_ret, ind_call.\n" +"-b Enable taken branch stack sampling. Same as '-j any'.\n" "-m mmap_pages Set the size of the buffer used to receiving sample data from\n" " the kernel. It should be a power of 2. If not set, the max\n" " possible value <= 1024 will be used.\n" @@ -139,8 +151,6 @@ class RecordCommand : public Command { " will be unwound by default. Use this option to disable the\n" " unwinding of the user's stack.\n" "-o record_file_name Set record file name, default is perf.data.\n" -"-p pid1,pid2,... Record events on existing processes. Mutually exclusive\n" -" with -a.\n" "--post-unwind If `--call-graph dwarf` option is used, then the user's stack\n" " will be unwound while recording by default. But it may lose\n" " records as stacking unwinding can be time consuming. Use this\n" @@ -150,8 +160,6 @@ class RecordCommand : public Command { "--symfs <dir> Look for files with symbols relative to this directory.\n" " This option is used to provide files with symbol table and\n" " debug information, which are used for unwinding and dumping symbols.\n" -"-t tid1,tid2,... Record events on existing threads. Mutually exclusive with -a.\n" -"--trace-offcpu Generate samples when threads are scheduled off cpu.\n" #if 0 // Below options are only used internally and shouldn't be visible to the public. "--in-app We are already running in the app's context.\n" @@ -159,10 +167,6 @@ class RecordCommand : public Command { #endif // clang-format on ), - use_sample_freq_(false), - sample_freq_(0), - use_sample_period_(false), - sample_period_(0), system_wide_collection_(false), branch_sampling_(0), fp_callchain_sampling_(false), @@ -218,11 +222,7 @@ class RecordCommand : public Command { bool DumpMetaInfoFeature(); void CollectHitFileInfo(const SampleRecord& r); - bool use_sample_freq_; - uint64_t sample_freq_; // Sample 'sample_freq_' times per second. - bool use_sample_period_; - uint64_t sample_period_; // Sample once when 'sample_period_' events occur. - + std::unique_ptr<SampleSpeed> sample_speed_; bool system_wide_collection_; uint64_t branch_sampling_; bool fp_callchain_sampling_; @@ -274,9 +274,13 @@ bool RecordCommand::Run(const std::vector<std::string>& args) { } } if (event_selection_set_.empty()) { - if (!event_selection_set_.AddEventType(default_measured_event_type)) { + size_t group_id; + if (!event_selection_set_.AddEventType(default_measured_event_type, &group_id)) { return false; } + if (sample_speed_) { + event_selection_set_.SetSampleSpeed(group_id, *sample_speed_); + } } exclude_kernel_callchain_ = event_selection_set_.ExcludeKernel(); if (trace_offcpu_ && !TraceOffCpu()) { @@ -422,6 +426,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) { bool RecordCommand::ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args) { + std::vector<size_t> wait_setting_speed_event_groups_; size_t i; for (i = 0; i < args.size() && !args[i].empty() && args[i][0] == '-'; ++i) { if (args[i] == "-a") { @@ -433,17 +438,26 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, app_package_name_ = args[i]; } else if (args[i] == "-b") { branch_sampling_ = branch_sampling_type_map["any"]; - } else if (args[i] == "-c") { + } else if (args[i] == "-c" || args[i] == "-f") { if (!NextArgumentOrError(args, &i)) { return false; } char* endptr; - sample_period_ = strtoull(args[i].c_str(), &endptr, 0); - if (*endptr != '\0' || sample_period_ == 0) { - LOG(ERROR) << "Invalid sample period: '" << args[i] << "'"; + uint64_t value = strtoull(args[i].c_str(), &endptr, 0); + if (*endptr != '\0' || value == 0) { + LOG(ERROR) << "Invalid option for " << args[i-1] << ": '" << args[i] << "'"; return false; } - use_sample_period_ = true; + if (args[i-1] == "-c") { + sample_speed_.reset(new SampleSpeed(0, value)); + } else { + sample_speed_.reset(new SampleSpeed(AdjustSampleFrequency(value), 0)); + } + for (auto group_id : wait_setting_speed_event_groups_) { + event_selection_set_.SetSampleSpeed(group_id, *sample_speed_); + } + wait_setting_speed_event_groups_.clear(); + } else if (args[i] == "--call-graph") { if (!NextArgumentOrError(args, &i)) { return false; @@ -501,20 +515,16 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, } std::vector<std::string> event_types = android::base::Split(args[i], ","); for (auto& event_type : event_types) { - if (!event_selection_set_.AddEventType(event_type)) { + size_t group_id; + if (!event_selection_set_.AddEventType(event_type, &group_id)) { return false; } + if (sample_speed_) { + event_selection_set_.SetSampleSpeed(group_id, *sample_speed_); + } else { + wait_setting_speed_event_groups_.push_back(group_id); + } } - } else if (args[i] == "-f" || args[i] == "-F") { - if (!NextArgumentOrError(args, &i)) { - return false; - } - if (!android::base::ParseUint(args[i].c_str(), &sample_freq_)) { - LOG(ERROR) << "Invalid sample frequency: " << args[i]; - return false; - } - sample_freq_ = AdjustSampleFrequency(sample_freq_); - use_sample_freq_ = true; } else if (args[i] == "-g") { fp_callchain_sampling_ = false; dwarf_callchain_sampling_ = true; @@ -523,9 +533,15 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, return false; } std::vector<std::string> event_types = android::base::Split(args[i], ","); - if (!event_selection_set_.AddEventGroup(event_types)) { + size_t group_id; + if (!event_selection_set_.AddEventGroup(event_types, &group_id)) { return false; } + if (sample_speed_) { + event_selection_set_.SetSampleSpeed(group_id, *sample_speed_); + } else { + wait_setting_speed_event_groups_.push_back(group_id); + } } else if (args[i] == "--in-app") { in_app_context_ = true; } else if (args[i] == "-j") { @@ -616,11 +632,6 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, } } - if (use_sample_freq_ && use_sample_period_) { - LOG(ERROR) << "-f option can't be used with -c option."; - return false; - } - if (!dwarf_callchain_sampling_) { if (!unwind_dwarf_callchain_) { LOG(ERROR) @@ -690,13 +701,6 @@ bool RecordCommand::TraceOffCpu() { } bool RecordCommand::SetEventSelectionFlags() { - if (use_sample_freq_) { - event_selection_set_.SetSampleFreq(sample_freq_); - } else if (use_sample_period_) { - event_selection_set_.SetSamplePeriod(sample_period_); - } else { - event_selection_set_.UseDefaultSampleFreq(); - } event_selection_set_.SampleIdAll(); if (!event_selection_set_.SetBranchSampling(branch_sampling_)) { return false; diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index 5f421379..48732302 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -61,8 +61,32 @@ TEST(record_cmd, system_wide_option) { TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"}))); } +void CheckEventType(const std::string& record_file, const std::string event_type, + uint64_t sample_period, uint64_t sample_freq) { + const EventType* type = FindEventTypeByName(event_type); + ASSERT_TRUE(type != nullptr); + std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(record_file); + ASSERT_TRUE(reader); + std::vector<EventAttrWithId> attrs = reader->AttrSection(); + for (auto& attr : attrs) { + if (attr.attr->type == type->type && attr.attr->config == type->config) { + if (attr.attr->freq == 0) { + ASSERT_EQ(sample_period, attr.attr->sample_period); + ASSERT_EQ(sample_freq, 0u); + } else { + ASSERT_EQ(sample_period, 0u); + ASSERT_EQ(sample_freq, attr.attr->sample_freq); + } + return; + } + } + FAIL(); +} + TEST(record_cmd, sample_period_option) { - ASSERT_TRUE(RunRecordCmd({"-c", "100000"})); + TemporaryFile tmpfile; + ASSERT_TRUE(RunRecordCmd({"-c", "100000"}, tmpfile.path)); + CheckEventType(tmpfile.path, "cpu-cycles", 100000u, 0); } TEST(record_cmd, event_option) { @@ -70,11 +94,22 @@ TEST(record_cmd, event_option) { } TEST(record_cmd, freq_option) { - ASSERT_TRUE(RunRecordCmd({"-f", "99"})); - ASSERT_TRUE(RunRecordCmd({"-F", "99"})); + TemporaryFile tmpfile; + ASSERT_TRUE(RunRecordCmd({"-f", "99"}, tmpfile.path)); + CheckEventType(tmpfile.path, "cpu-cycles", 0, 99u); + ASSERT_TRUE(RunRecordCmd({"-e", "cpu-clock", "-f", "99"}, tmpfile.path)); + CheckEventType(tmpfile.path, "cpu-clock", 0, 99u); ASSERT_TRUE(RunRecordCmd({"-f", std::to_string(UINT_MAX)})); } +TEST(record_cmd, multiple_freq_or_sample_period_option) { + TemporaryFile tmpfile; + ASSERT_TRUE(RunRecordCmd({"-f", "99", "-e", "cpu-cycles", "-c", "1000000", "-e", + "cpu-clock"}, tmpfile.path)); + CheckEventType(tmpfile.path, "cpu-cycles", 0, 99u); + CheckEventType(tmpfile.path, "cpu-clock", 1000000u, 0u); +} + TEST(record_cmd, output_file_option) { TemporaryFile tmpfile; ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", SLEEP_SEC})); @@ -415,7 +450,7 @@ TEST(record_cmd, record_meta_info_feature) { TemporaryFile tmpfile; ASSERT_TRUE(RunRecordCmd({}, tmpfile.path)); std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path); - ASSERT_TRUE(reader != nullptr); + ASSERT_TRUE(reader); std::unordered_map<std::string, std::string> info_map; ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map)); ASSERT_NE(info_map.find("simpleperf_version"), info_map.end()); @@ -443,10 +478,11 @@ TEST(record_cmd, trace_offcpu_option) { // On linux host, we need root privilege to read tracepoint events. TEST_REQUIRE_HOST_ROOT(); TemporaryFile tmpfile; - ASSERT_TRUE(RunRecordCmd({"--trace-offcpu"}, tmpfile.path)); + ASSERT_TRUE(RunRecordCmd({"--trace-offcpu", "-f", "1000"}, tmpfile.path)); std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path); - ASSERT_TRUE(reader != nullptr); + ASSERT_TRUE(reader); std::unordered_map<std::string, std::string> info_map; ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map)); ASSERT_EQ(info_map["trace_offcpu"], "true"); + CheckEventType(tmpfile.path, "sched:sched_switch", 1u, 0u); } diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp index 59607cb2..e0af5863 100644 --- a/simpleperf/event_selection_set.cpp +++ b/simpleperf/event_selection_set.cpp @@ -128,6 +128,14 @@ bool EventSelectionSet::BuildAndCheckEventSelection( selection->event_attr.exclude_host = event_type->exclude_host; selection->event_attr.exclude_guest = event_type->exclude_guest; selection->event_attr.precise_ip = event_type->precise_ip; + if (event_type->event_type.type == PERF_TYPE_TRACEPOINT) { + selection->event_attr.freq = 0; + selection->event_attr.sample_period = DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT; + } else { + selection->event_attr.freq = 1; + selection->event_attr.sample_freq = + AdjustSampleFrequency(DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT); + } if (!IsEventAttrSupported(selection->event_attr)) { LOG(ERROR) << "Event type '" << event_type->name << "' is not supported on the device"; @@ -147,12 +155,12 @@ bool EventSelectionSet::BuildAndCheckEventSelection( return true; } -bool EventSelectionSet::AddEventType(const std::string& event_name) { - return AddEventGroup(std::vector<std::string>(1, event_name)); +bool EventSelectionSet::AddEventType(const std::string& event_name, size_t* group_id) { + return AddEventGroup(std::vector<std::string>(1, event_name), group_id); } bool EventSelectionSet::AddEventGroup( - const std::vector<std::string>& event_names) { + const std::vector<std::string>& event_names, size_t* group_id) { EventSelectionGroup group; for (const auto& event_name : event_names) { EventSelection selection; @@ -163,6 +171,9 @@ bool EventSelectionSet::AddEventGroup( } groups_.push_back(std::move(group)); UnionSampleType(); + if (group_id != nullptr) { + *group_id = groups_.size() - 1; + } return true; } @@ -284,37 +295,15 @@ void EventSelectionSet::SampleIdAll() { } } -void EventSelectionSet::SetSampleFreq(uint64_t sample_freq) { - for (auto& group : groups_) { - for (auto& selection : group) { +void EventSelectionSet::SetSampleSpeed(size_t group_id, const SampleSpeed& speed) { + CHECK_LT(group_id, groups_.size()); + for (auto& selection : groups_[group_id]) { + if (speed.UseFreq()) { selection.event_attr.freq = 1; - selection.event_attr.sample_freq = sample_freq; - } - } -} - -void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) { - for (auto& group : groups_) { - for (auto& selection : group) { + selection.event_attr.sample_freq = speed.sample_freq; + } else { selection.event_attr.freq = 0; - selection.event_attr.sample_period = sample_period; - } - } -} - -void EventSelectionSet::UseDefaultSampleFreq() { - for (auto& group : groups_) { - for (auto& selection : group) { - if (selection.event_type_modifier.event_type.type == - PERF_TYPE_TRACEPOINT) { - selection.event_attr.freq = 0; - selection.event_attr.sample_period = - DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT; - } else { - selection.event_attr.freq = 1; - selection.event_attr.sample_freq = - DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT; - } + selection.event_attr.sample_period = speed.sample_period; } } } diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h index 3a3bd460..cc972bea 100644 --- a/simpleperf/event_selection_set.h +++ b/simpleperf/event_selection_set.h @@ -49,6 +49,20 @@ struct CountersInfo { std::vector<CounterInfo> counters; }; +struct SampleSpeed { + // There are two ways to set sample speed: + // 1. sample_freq: take [sample_freq] samples every second. + // 2. sample_period: take one sample every [sample_period] events happen. + uint64_t sample_freq; + uint64_t sample_period; + SampleSpeed(uint64_t freq = 0, uint64_t period = 0) : sample_freq(freq), sample_period(period) {} + bool UseFreq() const { + // Only use one way to set sample speed. + CHECK_NE(sample_freq != 0u, sample_period != 0u); + return sample_freq != 0u; + } +}; + // EventSelectionSet helps to monitor events. It is used in following steps: // 1. Create an EventSelectionSet, and add event types to monitor by calling // AddEventType() or AddEventGroup(). @@ -71,8 +85,8 @@ class EventSelectionSet { bool empty() const { return groups_.empty(); } - bool AddEventType(const std::string& event_name); - bool AddEventGroup(const std::vector<std::string>& event_names); + bool AddEventType(const std::string& event_name, size_t* group_id = nullptr); + bool AddEventGroup(const std::vector<std::string>& event_names, size_t* group_id = nullptr); std::vector<const EventType*> GetEvents() const; std::vector<const EventType*> GetTracepointEvents() const; bool ExcludeKernel() const; @@ -82,9 +96,7 @@ class EventSelectionSet { void SetEnableOnExec(bool enable); bool GetEnableOnExec(); void SampleIdAll(); - void SetSampleFreq(uint64_t sample_freq); - void SetSamplePeriod(uint64_t sample_period); - void UseDefaultSampleFreq(); + void SetSampleSpeed(size_t group_id, const SampleSpeed& speed); bool SetBranchSampling(uint64_t branch_sample_type); void EnableFpCallChainSampling(); bool EnableDwarfCallChainSampling(uint32_t dump_stack_size); |