summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2017-08-07 23:11:34 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-08-07 23:11:34 +0000
commitf9ed7b5507b4355021acc22adc14451e2e2d63ef (patch)
tree023d2c16bb9ff919ba93755adf310f745baef00f
parent6278bc9fd84a5a53c278357628bc947f2b91c3f5 (diff)
parent20b49f8991b55eda3309a0bbe3c18153376065da (diff)
downloadextras-f9ed7b5507b4355021acc22adc14451e2e2d63ef.tar.gz
Merge "simpleperf: allow recording events in different speed."
-rw-r--r--simpleperf/cmd_record.cpp128
-rw-r--r--simpleperf/cmd_record_test.cpp48
-rw-r--r--simpleperf/event_selection_set.cpp53
-rw-r--r--simpleperf/event_selection_set.h22
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);