" 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"
" 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"
" 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"
" 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"
"--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"
#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),
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_;
}
}
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()) {
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") {
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;
}
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;
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") {
}
}
- 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)
}
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;
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) {
}
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}));
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());
// 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);
}
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";
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;
}
groups_.push_back(std::move(group));
UnionSampleType();
+ if (group_id != nullptr) {
+ *group_id = groups_.size() - 1;
+ }
return true;
}
}
}
-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;
}
}
}