summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2016-06-13 18:03:47 -0700
committerYabin Cui <yabinc@google.com>2016-06-17 17:57:36 -0700
commit877751bbae11e009070e036ee1cd16e88135fff5 (patch)
treee0c795052ab4055a2d9bb0d2d2282305ff3b649d /simpleperf
parent90d56472a6981cbbeba5adfc293f920004cd9a22 (diff)
downloadextras-877751bbae11e009070e036ee1cd16e88135fff5.tar.gz
simpleperf: support event group.
Kernel supports monitoring several events as a group, so they are scheduled on and out at the same time. Add --group option to stat command and record command. Adjust the method to calculate miss rate in stat command: limit the matched events in the same group or with scale == 1.0. Bug: 29213742 Change-Id: I899aba207f1e3357307541e81f97526f5a2913c3
Diffstat (limited to 'simpleperf')
-rw-r--r--simpleperf/cmd_record.cpp152
-rw-r--r--simpleperf/cmd_record_test.cpp7
-rw-r--r--simpleperf/cmd_stat.cpp246
-rw-r--r--simpleperf/cmd_stat_test.cpp33
-rw-r--r--simpleperf/cpu_hotplug_test.cpp8
-rw-r--r--simpleperf/event_fd.cpp18
-rw-r--r--simpleperf/event_fd.h8
-rw-r--r--simpleperf/event_selection_set.cpp360
-rw-r--r--simpleperf/event_selection_set.h41
-rw-r--r--simpleperf/tracing.cpp15
-rw-r--r--simpleperf/tracing.h2
11 files changed, 511 insertions, 379 deletions
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index a989d37b..7bc5ec0e 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -95,6 +95,10 @@ class RecordCommand : public Command {
" 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"
"-j branch_filter1,branch_filter2,...\n"
" Enable taken branch stack sampling. Each sample captures a series\n"
" of consecutive taken branches.\n"
@@ -152,15 +156,14 @@ class RecordCommand : public Command {
private:
bool ParseOptions(const std::vector<std::string>& args,
std::vector<std::string>* non_option_args);
- bool AddMeasuredEventType(const std::string& event_type_name);
- bool SetEventSelection();
+ bool SetEventSelectionFlags();
bool CreateAndInitRecordFile();
std::unique_ptr<RecordFileWriter> CreateRecordFile(
const std::string& filename);
bool DumpKernelSymbol();
bool DumpTracingData();
- bool DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64_t event_id);
- bool DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t event_id,
+ bool DumpKernelAndModuleMmaps(const perf_event_attr& attr, uint64_t event_id);
+ bool DumpThreadCommAndMmaps(const perf_event_attr& attr, uint64_t event_id,
bool all_threads,
const std::vector<pid_t>& selected_threads);
bool ProcessRecord(Record* record);
@@ -190,7 +193,6 @@ class RecordCommand : public Command {
bool dump_symbols_;
std::vector<pid_t> monitored_threads_;
std::vector<int> cpus_;
- std::vector<EventTypeAndModifier> measured_event_types_;
EventSelectionSet event_selection_set_;
// mmap pages used by each perf event file, should be a power of 2.
@@ -217,12 +219,12 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
if (!ParseOptions(args, &workload_args)) {
return false;
}
- if (measured_event_types_.empty()) {
- if (!AddMeasuredEventType(default_measured_event_type)) {
+ if (event_selection_set_.empty()) {
+ if (!event_selection_set_.AddEventType(default_measured_event_type)) {
return false;
}
}
- if (!SetEventSelection()) {
+ if (!SetEventSelectionFlags()) {
return false;
}
@@ -369,7 +371,7 @@ 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 (!AddMeasuredEventType(event_type)) {
+ if (!event_selection_set_.AddEventType(event_type)) {
return false;
}
}
@@ -387,6 +389,14 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
} else if (args[i] == "-g") {
fp_callchain_sampling_ = false;
dwarf_callchain_sampling_ = true;
+ } else if (args[i] == "--group") {
+ if (!NextArgumentOrError(args, &i)) {
+ return false;
+ }
+ std::vector<std::string> event_types = android::base::Split(args[i], ",");
+ if (!event_selection_set_.AddEventGroup(event_types)) {
+ return false;
+ }
} else if (args[i] == "-j") {
if (!NextArgumentOrError(args, &i)) {
return false;
@@ -492,39 +502,22 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
return true;
}
-bool RecordCommand::AddMeasuredEventType(const std::string& event_type_name) {
- std::unique_ptr<EventTypeAndModifier> event_type_modifier =
- ParseEventType(event_type_name);
- if (event_type_modifier == nullptr) {
- return false;
- }
- for (const auto& type : measured_event_types_) {
- if (type.name == event_type_modifier->name) {
- return true;
- }
- }
- measured_event_types_.push_back(*event_type_modifier);
- return true;
-}
-
-bool RecordCommand::SetEventSelection() {
- for (auto& event_type : measured_event_types_) {
- if (!event_selection_set_.AddEventType(event_type)) {
- return false;
- }
- }
- for (auto& event_type : measured_event_types_) {
- if (use_sample_freq_) {
- event_selection_set_.SetSampleFreq(event_type, sample_freq_);
- } else if (use_sample_period_) {
- event_selection_set_.SetSamplePeriod(event_type, sample_period_);
- } else {
- if (event_type.event_type.type == PERF_TYPE_TRACEPOINT) {
- event_selection_set_.SetSamplePeriod(
- event_type, DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT);
+bool RecordCommand::SetEventSelectionFlags() {
+ for (const auto& group : event_selection_set_.groups()) {
+ for (const auto& selection : group) {
+ if (use_sample_freq_) {
+ event_selection_set_.SetSampleFreq(selection, sample_freq_);
+ } else if (use_sample_period_) {
+ event_selection_set_.SetSamplePeriod(selection, sample_period_);
} else {
- event_selection_set_.SetSampleFreq(
- event_type, DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT);
+ if (selection.event_type_modifier.event_type.type ==
+ PERF_TYPE_TRACEPOINT) {
+ event_selection_set_.SetSamplePeriod(
+ selection, DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT);
+ } else {
+ event_selection_set_.SetSampleFreq(
+ selection, DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT);
+ }
}
}
}
@@ -550,11 +543,10 @@ bool RecordCommand::CreateAndInitRecordFile() {
return false;
}
// Use first perf_event_attr and first event id to dump mmap and comm records.
- const perf_event_attr* attr =
- event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
- const std::vector<std::unique_ptr<EventFd>>* fds =
- event_selection_set_.FindEventFdsByType(measured_event_types_[0]);
- uint64_t event_id = (*fds)[0]->Id();
+ const EventSelection& selection = event_selection_set_.groups()[0][0];
+ const perf_event_attr& attr = selection.event_attr;
+ const std::vector<std::unique_ptr<EventFd>>& fds = selection.event_fds;
+ uint64_t event_id = fds[0]->Id();
if (!DumpKernelSymbol()) {
return false;
}
@@ -580,17 +572,17 @@ std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(
}
std::vector<AttrWithId> attr_ids;
- for (auto& event_type : measured_event_types_) {
- AttrWithId attr_id;
- attr_id.attr = event_selection_set_.FindEventAttrByType(event_type);
- CHECK(attr_id.attr != nullptr);
- const std::vector<std::unique_ptr<EventFd>>* fds =
- event_selection_set_.FindEventFdsByType(event_type);
- CHECK(fds != nullptr);
- for (auto& fd : *fds) {
- attr_id.ids.push_back(fd->Id());
- }
- attr_ids.push_back(attr_id);
+ for (const auto& group : event_selection_set_.groups()) {
+ for (const auto& selection : group) {
+ AttrWithId attr_id;
+ attr_id.attr = &selection.event_attr;
+ CHECK(attr_id.attr != nullptr);
+ const std::vector<std::unique_ptr<EventFd>>& fds = selection.event_fds;
+ for (const auto& fd : fds) {
+ attr_id.ids.push_back(fd->Id());
+ }
+ attr_ids.push_back(attr_id);
+ }
}
if (!writer->WriteAttrSection(attr_ids)) {
return nullptr;
@@ -602,10 +594,11 @@ bool RecordCommand::DumpKernelSymbol() {
if (can_dump_kernel_symbols_) {
std::string kallsyms;
bool need_kernel_symbol = false;
- for (const auto& type : measured_event_types_) {
- if (!type.exclude_kernel) {
- need_kernel_symbol = true;
- break;
+ for (const auto& group : event_selection_set_.groups()) {
+ for (const auto& selection : group) {
+ if (!selection.event_type_modifier.exclude_kernel) {
+ need_kernel_symbol = true;
+ }
}
}
if (need_kernel_symbol) {
@@ -626,18 +619,21 @@ bool RecordCommand::DumpKernelSymbol() {
}
bool RecordCommand::DumpTracingData() {
- bool has_tracepoint = false;
- for (const auto& type : measured_event_types_) {
- if (type.event_type.type == PERF_TYPE_TRACEPOINT) {
- has_tracepoint = true;
- break;
+ std::vector<const EventType*> tracepoint_event_types;
+ for (const auto& group : event_selection_set_.groups()) {
+ for (const auto& selection : group) {
+ if (selection.event_type_modifier.event_type.type ==
+ PERF_TYPE_TRACEPOINT) {
+ tracepoint_event_types.push_back(
+ &selection.event_type_modifier.event_type);
+ }
}
}
- if (!has_tracepoint) {
+ if (tracepoint_event_types.empty()) {
return true; // No need to dump tracing data.
}
std::vector<char> tracing_data;
- if (!GetTracingData(measured_event_types_, &tracing_data)) {
+ if (!GetTracingData(tracepoint_event_types, &tracing_data)) {
return false;
}
TracingDataRecord record = TracingDataRecord::Create(std::move(tracing_data));
@@ -647,21 +643,21 @@ bool RecordCommand::DumpTracingData() {
return true;
}
-bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr,
+bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr& attr,
uint64_t event_id) {
KernelMmap kernel_mmap;
std::vector<KernelMmap> module_mmaps;
GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps);
MmapRecord mmap_record =
- MmapRecord::Create(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
+ MmapRecord::Create(attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
kernel_mmap.len, 0, kernel_mmap.filepath, event_id);
if (!ProcessRecord(&mmap_record)) {
return false;
}
for (auto& module_mmap : module_mmaps) {
MmapRecord mmap_record =
- MmapRecord::Create(*attr, true, UINT_MAX, 0, module_mmap.start_addr,
+ MmapRecord::Create(attr, true, UINT_MAX, 0, module_mmap.start_addr,
module_mmap.len, 0, module_mmap.filepath, event_id);
if (!ProcessRecord(&mmap_record)) {
return false;
@@ -671,7 +667,7 @@ bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr,
}
bool RecordCommand::DumpThreadCommAndMmaps(
- const perf_event_attr* attr, uint64_t event_id, bool all_threads,
+ const perf_event_attr& attr, uint64_t event_id, bool all_threads,
const std::vector<pid_t>& selected_threads) {
std::vector<ThreadComm> thread_comms;
if (!GetThreadComms(&thread_comms)) {
@@ -698,8 +694,8 @@ bool RecordCommand::DumpThreadCommAndMmaps(
dump_processes.find(thread.pid) == dump_processes.end()) {
continue;
}
- CommRecord record = CommRecord::Create(*attr, thread.pid, thread.tid,
- thread.comm, event_id);
+ CommRecord record =
+ CommRecord::Create(attr, thread.pid, thread.tid, thread.comm, event_id);
if (!ProcessRecord(&record)) {
return false;
}
@@ -713,7 +709,7 @@ bool RecordCommand::DumpThreadCommAndMmaps(
continue; // No need to dump non-executable mmap info.
}
MmapRecord record = MmapRecord::Create(
- *attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
+ attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
thread_mmap.len, thread_mmap.pgoff, thread_mmap.name, event_id);
if (!ProcessRecord(&record)) {
return false;
@@ -730,12 +726,12 @@ bool RecordCommand::DumpThreadCommAndMmaps(
continue;
}
ForkRecord fork_record = ForkRecord::Create(
- *attr, thread.pid, thread.tid, thread.pid, thread.pid, event_id);
+ attr, thread.pid, thread.tid, thread.pid, thread.pid, event_id);
if (!ProcessRecord(&fork_record)) {
return false;
}
- CommRecord comm_record = CommRecord::Create(*attr, thread.pid, thread.tid,
- thread.comm, event_id);
+ CommRecord comm_record =
+ CommRecord::Create(attr, thread.pid, thread.tid, thread.comm, event_id);
if (!ProcessRecord(&comm_record)) {
return false;
}
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 244e6830..2306c556 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -296,3 +296,10 @@ TEST(record_cmd, dump_symbols) {
CheckDsoSymbolRecords(tmpfile.path, true, &success);
ASSERT_TRUE(success);
}
+
+TEST(record_cmd, group_option) {
+ ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock"}));
+ ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock", "--group",
+ "cpu-cycles:u,cpu-clock:u", "--group",
+ "cpu-cycles:k,cpu-clock:k"}));
+}
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index 15e74658..64ed60e0 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -38,6 +38,8 @@
#include "utils.h"
#include "workload.h"
+namespace {
+
static std::vector<std::string> default_measured_event_types{
"cpu-cycles", "stalled-cycles-frontend", "stalled-cycles-backend",
"instructions", "branch-instructions", "branch-misses",
@@ -45,33 +47,36 @@ static std::vector<std::string> default_measured_event_types{
};
static volatile bool signaled;
-static void signal_handler(int) {
- signaled = true;
-}
+static void signal_handler(int) { signaled = true; }
class StatCommand : public Command {
public:
StatCommand()
- : Command("stat", "gather performance counter information",
- "Usage: simpleperf stat [options] [command [command-args]]\n"
- " Gather performance counter information of running [command].\n"
- " -a Collect system-wide information.\n"
- " --cpu cpu_item1,cpu_item2,...\n"
- " Collect information only on the selected cpus. cpu_item can\n"
- " be a cpu number like 1, or a cpu range like 0-3.\n"
- " -e event1[:modifier1],event2[:modifier2],...\n"
- " Select the event list to count. Use `simpleperf list` to find\n"
- " all possible event names. Modifiers can be added to define\n"
- " how the event should be monitored. Possible modifiers are:\n"
- " u - monitor user space events only\n"
- " k - monitor kernel space events only\n"
- " --no-inherit\n"
- " Don't stat created child threads/processes.\n"
- " -p pid1,pid2,...\n"
- " Stat events on existing processes. Mutually exclusive with -a.\n"
- " -t tid1,tid2,...\n"
- " Stat events on existing threads. Mutually exclusive with -a.\n"
- " --verbose Show result in verbose mode.\n"),
+ : Command(
+ "stat", "gather performance counter information",
+ // clang-format off
+"Usage: simpleperf stat [options] [command [command-args]]\n"
+" Gather performance counter information of running [command].\n"
+"-a Collect system-wide information.\n"
+"--cpu cpu_item1,cpu_item2,...\n"
+" Collect information only on the selected cpus. cpu_item can\n"
+" be a cpu number like 1, or a cpu range like 0-3.\n"
+"-e event1[:modifier1],event2[:modifier2],...\n"
+" Select the event list to count. Use `simpleperf list` to find\n"
+" all possible event names. Modifiers can be added to define\n"
+" how the event should be monitored. Possible modifiers are:\n"
+" u - monitor user space events only\n"
+" k - monitor kernel space events only\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"
+"--no-inherit Don't stat created child threads/processes.\n"
+"-p pid1,pid2,... Stat events on existing processes. Mutually exclusive with -a.\n"
+"-t tid1,tid2,... Stat events on existing threads. Mutually exclusive with -a.\n"
+"--verbose Show result in verbose mode.\n"
+ // clang-format on
+ ),
verbose_mode_(false),
system_wide_collection_(false),
child_inherit_(true) {
@@ -83,18 +88,18 @@ class StatCommand : public Command {
bool Run(const std::vector<std::string>& args);
private:
- bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
- bool AddMeasuredEventType(const std::string& event_type_name);
+ bool ParseOptions(const std::vector<std::string>& args,
+ std::vector<std::string>* non_option_args);
bool AddDefaultMeasuredEventTypes();
- bool SetEventSelection();
- bool ShowCounters(const std::vector<CountersInfo>& counters, double duration_in_sec);
+ void SetEventSelectionFlags();
+ bool ShowCounters(const std::vector<CountersInfo>& counters,
+ double duration_in_sec);
bool verbose_mode_;
bool system_wide_collection_;
bool child_inherit_;
std::vector<pid_t> monitored_threads_;
std::vector<int> cpus_;
- std::vector<EventTypeAndModifier> measured_event_types_;
EventSelectionSet event_selection_set_;
std::unique_ptr<ScopedSignalHandler> scoped_signal_handler_;
@@ -110,14 +115,12 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
if (!ParseOptions(args, &workload_args)) {
return false;
}
- if (measured_event_types_.empty()) {
+ if (event_selection_set_.empty()) {
if (!AddDefaultMeasuredEventTypes()) {
return false;
}
}
- if (!SetEventSelection()) {
- return false;
- }
+ SetEventSelectionFlags();
// 2. Create workload.
std::unique_ptr<Workload> workload;
@@ -132,7 +135,8 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
monitored_threads_.push_back(workload->GetPid());
event_selection_set_.SetEnableOnExec(true);
} else {
- LOG(ERROR) << "No threads to monitor. Try `simpleperf help stat` for help\n";
+ LOG(ERROR)
+ << "No threads to monitor. Try `simpleperf help stat` for help\n";
return false;
}
}
@@ -146,7 +150,8 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
if (cpus_.empty()) {
cpus_ = {-1};
}
- if (!event_selection_set_.OpenEventFilesForThreadsOnCpus(monitored_threads_, cpus_)) {
+ if (!event_selection_set_.OpenEventFilesForThreadsOnCpus(monitored_threads_,
+ cpus_)) {
return false;
}
}
@@ -167,7 +172,9 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
return false;
}
double duration_in_sec =
- std::chrono::duration_cast<std::chrono::duration<double>>(end_time - start_time).count();
+ std::chrono::duration_cast<std::chrono::duration<double>>(end_time -
+ start_time)
+ .count();
if (!ShowCounters(counters, duration_in_sec)) {
return false;
}
@@ -192,10 +199,18 @@ bool StatCommand::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 (!AddMeasuredEventType(event_type)) {
+ if (!event_selection_set_.AddEventType(event_type)) {
return false;
}
}
+ } else if (args[i] == "--group") {
+ if (!NextArgumentOrError(args, &i)) {
+ return false;
+ }
+ std::vector<std::string> event_types = android::base::Split(args[i], ",");
+ if (!event_selection_set_.AddEventGroup(event_types)) {
+ return false;
+ }
} else if (args[i] == "--no-inherit") {
child_inherit_ = false;
} else if (args[i] == "-p") {
@@ -220,9 +235,11 @@ bool StatCommand::ParseOptions(const std::vector<std::string>& args,
}
}
- monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(), tid_set.end());
+ monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(),
+ tid_set.end());
if (system_wide_collection_ && !monitored_threads_.empty()) {
- LOG(ERROR) << "Stat system wide and existing processes/threads can't be used at the same time.";
+ LOG(ERROR) << "Stat system wide and existing processes/threads can't be "
+ "used at the same time.";
return false;
}
@@ -235,47 +252,31 @@ bool StatCommand::ParseOptions(const std::vector<std::string>& args,
return true;
}
-bool StatCommand::AddMeasuredEventType(const std::string& event_type_name) {
- std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_name);
- if (event_type_modifier == nullptr) {
- return false;
- }
- for (const auto& type : measured_event_types_) {
- if (type.name == event_type_modifier->name) {
- return true;
- }
- }
- measured_event_types_.push_back(*event_type_modifier);
- return true;
-}
-
bool StatCommand::AddDefaultMeasuredEventTypes() {
for (auto& name : default_measured_event_types) {
- // It is not an error when some event types in the default list are not supported by the kernel.
+ // It is not an error when some event types in the default list are not
+ // supported by the kernel.
const EventType* type = FindEventTypeByName(name);
- if (type != nullptr && IsEventAttrSupportedByKernel(CreateDefaultPerfEventAttr(*type))) {
- AddMeasuredEventType(name);
+ if (type != nullptr &&
+ IsEventAttrSupportedByKernel(CreateDefaultPerfEventAttr(*type))) {
+ if (!event_selection_set_.AddEventType(name)) {
+ return false;
+ }
}
}
- if (measured_event_types_.empty()) {
+ if (event_selection_set_.empty()) {
LOG(ERROR) << "Failed to add any supported default measured types";
return false;
}
return true;
}
-bool StatCommand::SetEventSelection() {
- for (auto& event_type : measured_event_types_) {
- if (!event_selection_set_.AddEventType(event_type)) {
- return false;
- }
- }
+void StatCommand::SetEventSelectionFlags() {
event_selection_set_.SetInherit(child_inherit_);
- return true;
}
-static std::string ReadableCountValue(uint64_t count,
- const EventTypeAndModifier& event_type_modifier) {
+static std::string ReadableCountValue(
+ uint64_t count, const EventTypeAndModifier& event_type_modifier) {
if (event_type_modifier.event_type.name == "cpu-clock" ||
event_type_modifier.event_type.name == "task-clock") {
double value = count / 1e6;
@@ -293,36 +294,57 @@ static std::string ReadableCountValue(uint64_t count,
}
struct CounterSummary {
- const EventTypeAndModifier* event_type;
+ const EventSelection* selection;
uint64_t count;
double scale;
std::string readable_count_str;
std::string comment;
};
-static std::string GetCommentForSummary(const CounterSummary& summary,
- const std::vector<CounterSummary>& summaries,
- double duration_in_sec) {
- const std::string& type_name = summary.event_type->event_type.name;
- const std::string& modifier = summary.event_type->modifier;
+static const CounterSummary* FindMatchedSummary(
+ const std::vector<CounterSummary>& summaries, const std::string& type_name,
+ const CounterSummary& summary) {
+ std::string modifier = summary.selection->event_type_modifier.modifier;
+ for (const auto& t : summaries) {
+ if (t.selection->event_type_modifier.event_type.name == type_name &&
+ t.selection->event_type_modifier.modifier == modifier) {
+ // The matched summary should be in the same group as summary,
+ // or both have scale == 1.0 (enabled all the time).
+ if (t.selection->group_id == summary.selection->group_id ||
+ (t.scale == 1.0 && summary.scale == 1.0)) {
+ return &t;
+ }
+ }
+ }
+ return nullptr;
+}
+
+static std::string GetCommentForSummary(
+ const CounterSummary& summary, const std::vector<CounterSummary>& summaries,
+ double duration_in_sec) {
+ const EventTypeAndModifier& type_modifier =
+ summary.selection->event_type_modifier;
+ const std::string& type_name = type_modifier.event_type.name;
if (type_name == "task-clock") {
double run_sec = summary.count / 1e9;
- double cpu_usage = run_sec / duration_in_sec;
- return android::base::StringPrintf("%lf%% cpu usage", cpu_usage * 100);
+ double used_cpus = run_sec / (duration_in_sec / summary.scale);
+ return android::base::StringPrintf("%lf cpus used", used_cpus);
}
if (type_name == "cpu-clock") {
return "";
}
if (type_name == "cpu-cycles") {
- double hz = summary.count / duration_in_sec;
+ double hz = summary.count / (duration_in_sec / summary.scale);
return android::base::StringPrintf("%lf GHz", hz / 1e9);
}
if (type_name == "instructions" && summary.count != 0) {
- for (auto& t : summaries) {
- if (t.event_type->event_type.name == "cpu-cycles" && t.event_type->modifier == modifier) {
- double cycles_per_instruction = t.count * 1.0 / summary.count;
- return android::base::StringPrintf("%lf cycles per instruction", cycles_per_instruction);
- }
+ const CounterSummary* cpu_cycles_summary =
+ FindMatchedSummary(summaries, "cpu-cycles", summary);
+ if (cpu_cycles_summary != nullptr) {
+ double cycles_per_instruction =
+ static_cast<double>(cpu_cycles_summary->count) / summary.count;
+ return android::base::StringPrintf("%lf cycles per instruction",
+ cycles_per_instruction);
}
}
if (android::base::EndsWith(type_name, "-misses")) {
@@ -334,14 +356,15 @@ static std::string GetCommentForSummary(const CounterSummary& summary,
} else {
s = type_name.substr(0, type_name.size() - strlen("-misses")) + "s";
}
- for (auto& t : summaries) {
- if (t.event_type->event_type.name == s && t.event_type->modifier == modifier && t.count != 0) {
- double miss_rate = summary.count * 1.0 / t.count;
- return android::base::StringPrintf("%lf%% miss rate", miss_rate * 100);
- }
+ const CounterSummary* matched_summary =
+ FindMatchedSummary(summaries, s, summary);
+ if (matched_summary != nullptr && matched_summary->count != 0) {
+ double miss_rate =
+ static_cast<double>(summary.count) / matched_summary->count;
+ return android::base::StringPrintf("%lf%% miss rate", miss_rate * 100);
}
}
- double rate = summary.count / duration_in_sec;
+ double rate = summary.count / (duration_in_sec / summary.scale);
if (rate > 1e9) {
return android::base::StringPrintf("%.3lf G/sec", rate / 1e9);
}
@@ -354,19 +377,22 @@ static std::string GetCommentForSummary(const CounterSummary& summary,
return android::base::StringPrintf("%.3lf /sec", rate);
}
-bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters, double duration_in_sec) {
+bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters,
+ double duration_in_sec) {
printf("Performance counter statistics:\n\n");
if (verbose_mode_) {
for (auto& counters_info : counters) {
- const EventTypeAndModifier* event_type = counters_info.event_type;
+ const EventTypeAndModifier& event_type =
+ counters_info.selection->event_type_modifier;
for (auto& counter_info : counters_info.counters) {
- printf("%s(tid %d, cpu %d): count %s, time_enabled %" PRIu64 ", time running %" PRIu64
- ", id %" PRIu64 "\n",
- event_type->name.c_str(), counter_info.tid, counter_info.cpu,
- ReadableCountValue(counter_info.counter.value, *event_type).c_str(),
- counter_info.counter.time_enabled, counter_info.counter.time_running,
- counter_info.counter.id);
+ printf(
+ "%s(tid %d, cpu %d): count %s, time_enabled %" PRIu64
+ ", time running %" PRIu64 ", id %" PRIu64 "\n",
+ event_type.name.c_str(), counter_info.tid, counter_info.cpu,
+ ReadableCountValue(counter_info.counter.value, event_type).c_str(),
+ counter_info.counter.time_enabled,
+ counter_info.counter.time_running, counter_info.counter.id);
}
}
}
@@ -377,8 +403,8 @@ bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters, double
uint64_t time_enabled_sum = 0;
uint64_t time_running_sum = 0;
for (auto& counter_info : counters_info.counters) {
- // If time_running is 0, the program has never run on this event and we shouldn't
- // summarize it.
+ // If time_running is 0, the program has never run on this event and we
+ // shouldn't summarize it.
if (counter_info.counter.time_running != 0) {
value_sum += counter_info.counter.value;
time_enabled_sum += counter_info.counter.time_enabled;
@@ -390,10 +416,11 @@ bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters, double
scale = static_cast<double>(time_enabled_sum) / time_running_sum;
}
CounterSummary summary;
- summary.event_type = counters_info.event_type;
+ summary.selection = counters_info.selection;
summary.count = value_sum;
summary.scale = scale;
- summary.readable_count_str = ReadableCountValue(summary.count, *summary.event_type);
+ summary.readable_count_str = ReadableCountValue(
+ summary.count, summary.selection->event_type_modifier);
summaries.push_back(summary);
}
@@ -405,22 +432,31 @@ bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters, double
size_t name_column_width = 0;
size_t comment_column_width = 0;
for (auto& summary : summaries) {
- count_column_width = std::max(count_column_width, summary.readable_count_str.size());
- name_column_width = std::max(name_column_width, summary.event_type->name.size());
- comment_column_width = std::max(comment_column_width, summary.comment.size());
+ count_column_width =
+ std::max(count_column_width, summary.readable_count_str.size());
+ name_column_width = std::max(
+ name_column_width, summary.selection->event_type_modifier.name.size());
+ comment_column_width =
+ std::max(comment_column_width, summary.comment.size());
}
for (auto& summary : summaries) {
- printf(" %*s %-*s # %-*s (%.0lf%%)\n", static_cast<int>(count_column_width),
- summary.readable_count_str.c_str(), static_cast<int>(name_column_width),
- summary.event_type->name.c_str(), static_cast<int>(comment_column_width),
- summary.comment.c_str(), 1.0 / summary.scale * 100);
+ printf(" %*s %-*s # %-*s (%.0lf%%)\n",
+ static_cast<int>(count_column_width),
+ summary.readable_count_str.c_str(),
+ static_cast<int>(name_column_width),
+ summary.selection->event_type_modifier.name.c_str(),
+ static_cast<int>(comment_column_width), summary.comment.c_str(),
+ 1.0 / summary.scale * 100);
}
printf("\nTotal test time: %lf seconds.\n", duration_in_sec);
return true;
}
+} // namespace
+
void RegisterStatCommand() {
- RegisterCommand("stat", [] { return std::unique_ptr<Command>(new StatCommand); });
+ RegisterCommand("stat",
+ [] { return std::unique_ptr<Command>(new StatCommand); });
}
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 1935b5fe..0bb7d855 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -26,9 +26,7 @@ static std::unique_ptr<Command> StatCmd() {
return CreateCommandInstance("stat");
}
-TEST(stat_cmd, no_options) {
- ASSERT_TRUE(StatCmd()->Run({"sleep", "1"}));
-}
+TEST(stat_cmd, no_options) { ASSERT_TRUE(StatCmd()->Run({"sleep", "1"})); }
TEST(stat_cmd, event_option) {
ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-clock,task-clock", "sleep", "1"}));
@@ -43,14 +41,17 @@ TEST(stat_cmd, verbose_option) {
}
TEST(stat_cmd, tracepoint_event) {
- TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"})));
+ TEST_IN_ROOT(ASSERT_TRUE(
+ StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"})));
}
TEST(stat_cmd, event_modifier) {
- ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-cycles:u,cpu-cycles:k", "sleep", "1"}));
+ ASSERT_TRUE(
+ StatCmd()->Run({"-e", "cpu-cycles:u,cpu-cycles:k", "sleep", "1"}));
}
-void CreateProcesses(size_t count, std::vector<std::unique_ptr<Workload>>* workloads) {
+void CreateProcesses(size_t count,
+ std::vector<std::unique_ptr<Workload>>* workloads) {
workloads->clear();
for (size_t i = 0; i < count; ++i) {
auto workload = Workload::CreateWorkload({"sleep", "1"});
@@ -63,8 +64,8 @@ void CreateProcesses(size_t count, std::vector<std::unique_ptr<Workload>>* workl
TEST(stat_cmd, existing_processes) {
std::vector<std::unique_ptr<Workload>> workloads;
CreateProcesses(2, &workloads);
- std::string pid_list =
- android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
+ std::string pid_list = android::base::StringPrintf(
+ "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
ASSERT_TRUE(StatCmd()->Run({"-p", pid_list}));
}
@@ -72,16 +73,22 @@ TEST(stat_cmd, existing_threads) {
std::vector<std::unique_ptr<Workload>> workloads;
CreateProcesses(2, &workloads);
// Process id can be used as thread id in linux.
- std::string tid_list =
- android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
+ std::string tid_list = android::base::StringPrintf(
+ "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
ASSERT_TRUE(StatCmd()->Run({"-t", tid_list}));
}
-TEST(stat_cmd, no_monitored_threads) {
- ASSERT_FALSE(StatCmd()->Run({""}));
-}
+TEST(stat_cmd, no_monitored_threads) { ASSERT_FALSE(StatCmd()->Run({""})); }
TEST(stat_cmd, cpu_option) {
ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "sleep", "1"}));
TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "-a", "sleep", "1"})));
}
+
+TEST(stat_cmd, group_option) {
+ ASSERT_TRUE(
+ StatCmd()->Run({"--group", "cpu-cycles,cpu-clock", "sleep", "1"}));
+ ASSERT_TRUE(StatCmd()->Run({"--group", "cpu-cycles,cpu-clock", "--group",
+ "cpu-cycles:u,cpu-clock:u", "--group",
+ "cpu-cycles:k,cpu-clock:k", "sleep", "1"}));
+}
diff --git a/simpleperf/cpu_hotplug_test.cpp b/simpleperf/cpu_hotplug_test.cpp
index 2cc9a0e0..56962b16 100644
--- a/simpleperf/cpu_hotplug_test.cpp
+++ b/simpleperf/cpu_hotplug_test.cpp
@@ -232,7 +232,7 @@ TEST(cpu_offline, offline_while_recording) {
size_t iterations = 0;
while (std::chrono::steady_clock::now() < end_time) {
- std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, -1, test_cpu, false);
+ std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, -1, test_cpu, nullptr, false);
if (event_fd == nullptr) {
// Failed to open because the test_cpu is offline.
continue;
@@ -273,7 +273,7 @@ TEST(cpu_offline, offline_while_ioctl_enable) {
size_t iterations = 0;
while (std::chrono::steady_clock::now() < end_time) {
- std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, -1, test_cpu, false);
+ std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, -1, test_cpu, nullptr, false);
if (event_fd == nullptr) {
// Failed to open because the test_cpu is offline.
continue;
@@ -310,11 +310,11 @@ TEST(cpu_offline, offline_while_recording_on_another_cpu) {
for (size_t i = 0; i < TEST_ITERATION_COUNT; ++i) {
int record_cpu = 0;
ASSERT_TRUE(SetCpuOnline(test_cpu, true));
- std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu);
+ std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu, nullptr);
ASSERT_TRUE(event_fd != nullptr);
ASSERT_TRUE(SetCpuOnline(test_cpu, false));
event_fd = nullptr;
- event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu);
+ event_fd = EventFd::OpenEventFile(attr, getpid(), record_cpu, nullptr);
ASSERT_TRUE(event_fd != nullptr);
}
}
diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp
index 705d6f35..58e04dc6 100644
--- a/simpleperf/event_fd.cpp
+++ b/simpleperf/event_fd.cpp
@@ -44,27 +44,31 @@ static int perf_event_open(perf_event_attr* attr, pid_t pid, int cpu, int group_
}
std::unique_ptr<EventFd> EventFd::OpenEventFile(const perf_event_attr& attr, pid_t tid, int cpu,
- bool report_error) {
+ EventFd* group_event_fd, bool report_error) {
perf_event_attr perf_attr = attr;
std::string event_name = GetEventNameByAttr(attr);
- int perf_event_fd = perf_event_open(&perf_attr, tid, cpu, -1, 0);
+ int group_fd = -1;
+ if (group_event_fd != nullptr) {
+ group_fd = group_event_fd->perf_event_fd_;
+ }
+ int perf_event_fd = perf_event_open(&perf_attr, tid, cpu, group_fd, 0);
if (perf_event_fd == -1) {
if (report_error) {
PLOG(ERROR) << "open perf_event_file (event " << event_name << ", tid " << tid << ", cpu "
- << cpu << ") failed";
+ << cpu << ", group_fd " << group_fd << ") failed";
} else {
PLOG(DEBUG) << "open perf_event_file (event " << event_name << ", tid " << tid << ", cpu "
- << cpu << ") failed";
+ << cpu << ", group_fd " << group_fd << ") failed";
}
return nullptr;
}
if (fcntl(perf_event_fd, F_SETFD, FD_CLOEXEC) == -1) {
if (report_error) {
PLOG(ERROR) << "fcntl(FD_CLOEXEC) for perf_event_file (event " << event_name << ", tid "
- << tid << ", cpu " << cpu << ") failed";
+ << tid << ", cpu " << cpu << ", group_fd " << group_fd << ") failed";
} else {
PLOG(DEBUG) << "fcntl(FD_CLOEXEC) for perf_event_file (event " << event_name << ", tid "
- << tid << ", cpu " << cpu << ") failed";
+ << tid << ", cpu " << cpu << ", group_fd " << group_fd << ") failed";
}
return nullptr;
}
@@ -190,6 +194,6 @@ void EventFd::PrepareToPollForMmapData(pollfd* poll_fd) {
}
bool IsEventAttrSupportedByKernel(perf_event_attr attr) {
- auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, false);
+ auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, nullptr, false);
return event_fd != nullptr;
}
diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h
index f06d2602..c54c3e61 100644
--- a/simpleperf/event_fd.h
+++ b/simpleperf/event_fd.h
@@ -40,10 +40,13 @@ struct pollfd;
class EventFd {
public:
static std::unique_ptr<EventFd> OpenEventFile(const perf_event_attr& attr, pid_t tid, int cpu,
- bool report_error = true);
+ EventFd* group_event_fd, bool report_error = true);
~EventFd();
+ // Give information about this perf_event_file, like (event_name, tid, cpu).
+ std::string Name() const;
+
uint64_t Id() const;
pid_t ThreadId() const {
@@ -81,9 +84,6 @@ class EventFd {
mmap_len_(0) {
}
- // Give information about this perf_event_file, like (event_name, tid, cpu).
- std::string Name() const;
-
// Discard how much data we have read, so the kernel can reuse this part of mapped area to store
// new data.
void DiscardMmapData(size_t discard_size);
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 92e559c5..1c832505 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -43,115 +43,170 @@ bool IsDwarfCallChainSamplingSupported() {
return false;
}
perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
- attr.sample_type |= PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER;
+ attr.sample_type |=
+ PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER;
attr.exclude_callchain_user = 1;
attr.sample_regs_user = GetSupportedRegMask(GetBuildArch());
attr.sample_stack_user = 8192;
return IsEventAttrSupportedByKernel(attr);
}
-bool EventSelectionSet::AddEventType(const EventTypeAndModifier& event_type_modifier) {
- EventSelection selection;
- selection.event_type_modifier = event_type_modifier;
- selection.event_attr = CreateDefaultPerfEventAttr(event_type_modifier.event_type);
- selection.event_attr.exclude_user = event_type_modifier.exclude_user;
- selection.event_attr.exclude_kernel = event_type_modifier.exclude_kernel;
- selection.event_attr.exclude_hv = event_type_modifier.exclude_hv;
- selection.event_attr.exclude_host = event_type_modifier.exclude_host;
- selection.event_attr.exclude_guest = event_type_modifier.exclude_guest;
- selection.event_attr.precise_ip = event_type_modifier.precise_ip;
- if (!IsEventAttrSupportedByKernel(selection.event_attr)) {
- LOG(ERROR) << "Event type '" << event_type_modifier.name << "' is not supported by the kernel";
+bool EventSelectionSet::BuildAndCheckEventSelection(
+ const std::string& event_name, EventSelection* selection) {
+ std::unique_ptr<EventTypeAndModifier> event_type = ParseEventType(event_name);
+ if (event_type == nullptr) {
return false;
}
- selections_.push_back(std::move(selection));
+ selection->event_type_modifier = *event_type;
+ selection->event_attr = CreateDefaultPerfEventAttr(event_type->event_type);
+ selection->event_attr.exclude_user = event_type->exclude_user;
+ selection->event_attr.exclude_kernel = event_type->exclude_kernel;
+ selection->event_attr.exclude_hv = event_type->exclude_hv;
+ 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 (!IsEventAttrSupportedByKernel(selection->event_attr)) {
+ LOG(ERROR) << "Event type '" << event_type->name
+ << "' is not supported by the kernel";
+ return false;
+ }
+ selection->event_fds.clear();
+
+ for (const auto& group : groups_) {
+ for (const auto& sel : group) {
+ if (sel.event_type_modifier.name == selection->event_type_modifier.name) {
+ LOG(ERROR) << "Event type '" << sel.event_type_modifier.name
+ << "' appears more than once";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool EventSelectionSet::AddEventType(const std::string& event_name) {
+ return AddEventGroup(std::vector<std::string>(1, event_name));
+}
+
+bool EventSelectionSet::AddEventGroup(
+ const std::vector<std::string>& event_names) {
+ EventSelectionGroup group;
+ for (const auto& event_name : event_names) {
+ EventSelection selection;
+ if (!BuildAndCheckEventSelection(event_name, &selection)) {
+ return false;
+ }
+ selection.selection_id = group.size();
+ selection.group_id = groups_.size();
+ group.push_back(std::move(selection));
+ }
+ groups_.push_back(std::move(group));
UnionSampleType();
return true;
}
-// Union the sample type of different event attrs can make reading sample records in perf.data
+// Union the sample type of different event attrs can make reading sample
+// records in perf.data
// easier.
void EventSelectionSet::UnionSampleType() {
uint64_t sample_type = 0;
- for (auto& selection : selections_) {
- sample_type |= selection.event_attr.sample_type;
+ for (const auto& group : groups_) {
+ for (const auto& selection : group) {
+ sample_type |= selection.event_attr.sample_type;
+ }
}
- for (auto& selection : selections_) {
- selection.event_attr.sample_type = sample_type;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ selection.event_attr.sample_type = sample_type;
+ }
}
}
void EventSelectionSet::SetEnableOnExec(bool enable) {
- for (auto& selection : selections_) {
- // If sampling is enabled on exec, then it is disabled at startup, otherwise
- // it should be enabled at startup. Don't use ioctl(PERF_EVENT_IOC_ENABLE)
- // to enable it after perf_event_open(). Because some android kernels can't
- // handle ioctl() well when cpu-hotplug happens. See http://b/25193162.
- if (enable) {
- selection.event_attr.enable_on_exec = 1;
- selection.event_attr.disabled = 1;
- } else {
- selection.event_attr.enable_on_exec = 0;
- selection.event_attr.disabled = 0;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ // If sampling is enabled on exec, then it is disabled at startup,
+ // otherwise
+ // it should be enabled at startup. Don't use ioctl(PERF_EVENT_IOC_ENABLE)
+ // to enable it after perf_event_open(). Because some android kernels
+ // can't
+ // handle ioctl() well when cpu-hotplug happens. See http://b/25193162.
+ if (enable) {
+ selection.event_attr.enable_on_exec = 1;
+ selection.event_attr.disabled = 1;
+ } else {
+ selection.event_attr.enable_on_exec = 0;
+ selection.event_attr.disabled = 0;
+ }
}
}
}
bool EventSelectionSet::GetEnableOnExec() {
- for (auto& selection : selections_) {
- if (selection.event_attr.enable_on_exec == 0) {
- return false;
+ for (const auto& group : groups_) {
+ for (const auto& selection : group) {
+ if (selection.event_attr.enable_on_exec == 0) {
+ return false;
+ }
}
}
return true;
}
void EventSelectionSet::SampleIdAll() {
- for (auto& selection : selections_) {
- selection.event_attr.sample_id_all = 1;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ selection.event_attr.sample_id_all = 1;
+ }
}
}
-void EventSelectionSet::SetSampleFreq(const EventTypeAndModifier& event_type_modifier, uint64_t sample_freq) {
- EventSelection* sel = FindSelectionByType(event_type_modifier);
- CHECK(sel != nullptr);
- sel->event_attr.freq = 1;
- sel->event_attr.sample_freq = sample_freq;
+void EventSelectionSet::SetSampleFreq(const EventSelection& selection,
+ uint64_t sample_freq) {
+ EventSelection& sel = groups_[selection.group_id][selection.selection_id];
+ sel.event_attr.freq = 1;
+ sel.event_attr.sample_freq = sample_freq;
}
-void EventSelectionSet::SetSamplePeriod(const EventTypeAndModifier& event_type_modifier, uint64_t sample_period) {
- EventSelection* sel = FindSelectionByType(event_type_modifier);
- CHECK(sel != nullptr);
- sel->event_attr.freq = 0;
- sel->event_attr.sample_period = sample_period;
+void EventSelectionSet::SetSamplePeriod(const EventSelection& selection,
+ uint64_t sample_period) {
+ EventSelection& sel = groups_[selection.group_id][selection.selection_id];
+ sel.event_attr.freq = 0;
+ sel.event_attr.sample_period = sample_period;
}
bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
if (branch_sample_type != 0 &&
- (branch_sample_type & (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
- PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) {
- LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex << branch_sample_type;
+ (branch_sample_type &
+ (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
+ PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) {
+ LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex
+ << branch_sample_type;
return false;
}
if (branch_sample_type != 0 && !IsBranchSamplingSupported()) {
LOG(ERROR) << "branch stack sampling is not supported on this device.";
return false;
}
- for (auto& selection : selections_) {
- perf_event_attr& attr = selection.event_attr;
- if (branch_sample_type != 0) {
- attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
- } else {
- attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ perf_event_attr& attr = selection.event_attr;
+ if (branch_sample_type != 0) {
+ attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
+ } else {
+ attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK;
+ }
+ attr.branch_sample_type = branch_sample_type;
}
- attr.branch_sample_type = branch_sample_type;
}
return true;
}
void EventSelectionSet::EnableFpCallChainSampling() {
- for (auto& selection : selections_) {
- selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
+ }
}
}
@@ -160,26 +215,33 @@ bool EventSelectionSet::EnableDwarfCallChainSampling(uint32_t dump_stack_size) {
LOG(ERROR) << "dwarf callchain sampling is not supported on this device.";
return false;
}
- for (auto& selection : selections_) {
- selection.event_attr.sample_type |=
- PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER;
- selection.event_attr.exclude_callchain_user = 1;
- selection.event_attr.sample_regs_user = GetSupportedRegMask(GetBuildArch());
- selection.event_attr.sample_stack_user = dump_stack_size;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN |
+ PERF_SAMPLE_REGS_USER |
+ PERF_SAMPLE_STACK_USER;
+ selection.event_attr.exclude_callchain_user = 1;
+ selection.event_attr.sample_regs_user =
+ GetSupportedRegMask(GetBuildArch());
+ selection.event_attr.sample_stack_user = dump_stack_size;
+ }
}
return true;
}
void EventSelectionSet::SetInherit(bool enable) {
- for (auto& selection : selections_) {
- selection.event_attr.inherit = (enable ? 1 : 0);
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ selection.event_attr.inherit = (enable ? 1 : 0);
+ }
}
}
static bool CheckIfCpusOnline(const std::vector<int>& cpus) {
std::vector<int> online_cpus = GetOnlineCpus();
for (const auto& cpu : cpus) {
- if (std::find(online_cpus.begin(), online_cpus.end(), cpu) == online_cpus.end()) {
+ if (std::find(online_cpus.begin(), online_cpus.end(), cpu) ==
+ online_cpus.end()) {
LOG(ERROR) << "cpu " << cpu << " is not online.";
return false;
}
@@ -191,8 +253,8 @@ bool EventSelectionSet::OpenEventFilesForCpus(const std::vector<int>& cpus) {
return OpenEventFilesForThreadsOnCpus({-1}, cpus);
}
-bool EventSelectionSet::OpenEventFilesForThreadsOnCpus(const std::vector<pid_t>& threads,
- std::vector<int> cpus) {
+bool EventSelectionSet::OpenEventFilesForThreadsOnCpus(
+ const std::vector<pid_t>& threads, std::vector<int> cpus) {
if (!cpus.empty()) {
// cpus = {-1} means open an event file for all cpus.
if (!(cpus.size() == 1 && cpus[0] == -1) && !CheckIfCpusOnline(cpus)) {
@@ -206,23 +268,45 @@ bool EventSelectionSet::OpenEventFilesForThreadsOnCpus(const std::vector<pid_t>&
bool EventSelectionSet::OpenEventFiles(const std::vector<pid_t>& threads,
const std::vector<int>& cpus) {
- for (auto& selection : selections_) {
- for (auto& tid : threads) {
+ for (auto& group : groups_) {
+ for (const auto& tid : threads) {
size_t open_per_thread = 0;
- for (auto& cpu : cpus) {
- auto event_fd = EventFd::OpenEventFile(selection.event_attr, tid, cpu);
- if (event_fd != nullptr) {
- LOG(VERBOSE) << "OpenEventFile for tid " << tid << ", cpu " << cpu;
- selection.event_fds.push_back(std::move(event_fd));
+ std::string failed_event_type;
+ for (const auto& cpu : cpus) {
+ std::vector<std::unique_ptr<EventFd>> event_fds;
+ // Given a tid and cpu, events on the same group should be all opened
+ // successfully or all failed to open.
+ for (auto& selection : group) {
+ EventFd* group_fd = nullptr;
+ if (selection.selection_id != 0) {
+ group_fd = event_fds[0].get();
+ }
+ std::unique_ptr<EventFd> event_fd =
+ EventFd::OpenEventFile(selection.event_attr, tid, cpu, group_fd);
+ if (event_fd != nullptr) {
+ LOG(VERBOSE) << "OpenEventFile for " << event_fd->Name();
+ event_fds.push_back(std::move(event_fd));
+ } else {
+ failed_event_type = selection.event_type_modifier.name;
+ break;
+ }
+ }
+ if (event_fds.size() == group.size()) {
+ for (size_t i = 0; i < group.size(); ++i) {
+ group[i].event_fds.push_back(std::move(event_fds[i]));
+ }
++open_per_thread;
}
}
- // As the online cpus can be enabled or disabled at runtime, we may not open event file for
- // all cpus successfully. But we should open at least one cpu successfully.
+ // As the online cpus can be enabled or disabled at runtime, we may not
+ // open event file for
+ // all cpus successfully. But we should open at least one cpu
+ // successfully.
if (open_per_thread == 0) {
PLOG(ERROR) << "failed to open perf event file for event_type "
- << selection.event_type_modifier.name << " for "
- << (tid == -1 ? "all threads" : android::base::StringPrintf(" thread %d", tid));
+ << failed_event_type << " for "
+ << (tid == -1 ? "all threads" : android::base::StringPrintf(
+ " thread %d", tid));
return false;
}
}
@@ -232,70 +316,85 @@ bool EventSelectionSet::OpenEventFiles(const std::vector<pid_t>& threads,
bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) {
counters->clear();
- for (auto& selection : selections_) {
- CountersInfo counters_info;
- counters_info.event_type = &selection.event_type_modifier;
- for (auto& event_fd : selection.event_fds) {
- CountersInfo::CounterInfo counter_info;
- if (!event_fd->ReadCounter(&counter_info.counter)) {
- return false;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ CountersInfo counters_info;
+ counters_info.selection = &selection;
+ for (auto& event_fd : selection.event_fds) {
+ CountersInfo::CounterInfo counter_info;
+ if (!event_fd->ReadCounter(&counter_info.counter)) {
+ return false;
+ }
+ counter_info.tid = event_fd->ThreadId();
+ counter_info.cpu = event_fd->Cpu();
+ counters_info.counters.push_back(counter_info);
}
- counter_info.tid = event_fd->ThreadId();
- counter_info.cpu = event_fd->Cpu();
- counters_info.counters.push_back(counter_info);
+ counters->push_back(counters_info);
}
- counters->push_back(counters_info);
}
return true;
}
-void EventSelectionSet::PrepareToPollForEventFiles(std::vector<pollfd>* pollfds) {
- for (auto& selection : selections_) {
- for (auto& event_fd : selection.event_fds) {
- pollfd poll_fd;
- event_fd->PrepareToPollForMmapData(&poll_fd);
- pollfds->push_back(poll_fd);
+void EventSelectionSet::PrepareToPollForEventFiles(
+ std::vector<pollfd>* pollfds) {
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ for (auto& event_fd : selection.event_fds) {
+ pollfd poll_fd;
+ event_fd->PrepareToPollForMmapData(&poll_fd);
+ pollfds->push_back(poll_fd);
+ }
}
}
}
bool EventSelectionSet::MmapEventFiles(size_t mmap_pages) {
- for (auto& selection : selections_) {
- for (auto& event_fd : selection.event_fds) {
- if (!event_fd->MmapContent(mmap_pages)) {
- return false;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ for (auto& event_fd : selection.event_fds) {
+ if (!event_fd->MmapContent(mmap_pages)) {
+ return false;
+ }
}
}
}
return true;
}
-void EventSelectionSet::PrepareToReadMmapEventData(std::function<bool (Record*)> callback) {
+void EventSelectionSet::PrepareToReadMmapEventData(
+ std::function<bool(Record*)> callback) {
record_callback_ = callback;
bool has_timestamp = true;
- for (const auto& selection : selections_) {
- if (!IsTimestampSupported(selection.event_attr)) {
- has_timestamp = false;
- break;
+ for (const auto& group : groups_) {
+ for (const auto& selection : group) {
+ if (!IsTimestampSupported(selection.event_attr)) {
+ has_timestamp = false;
+ break;
+ }
}
}
record_cache_.reset(new RecordCache(has_timestamp));
- for (const auto& selection : selections_) {
- for (const auto& event_fd : selection.event_fds) {
- int event_id = event_fd->Id();
- event_id_to_attr_map_[event_id] = &selection.event_attr;
+ for (const auto& group : groups_) {
+ for (const auto& selection : group) {
+ for (const auto& event_fd : selection.event_fds) {
+ int event_id = event_fd->Id();
+ event_id_to_attr_map_[event_id] = &selection.event_attr;
+ }
}
}
}
bool EventSelectionSet::ReadMmapEventData() {
- for (auto& selection : selections_) {
- for (auto& event_fd : selection.event_fds) {
- bool has_data = true;
- while (has_data) {
- if (!ReadMmapEventDataForFd(event_fd, selection.event_attr, &has_data)) {
- return false;
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ for (auto& event_fd : selection.event_fds) {
+ bool has_data = true;
+ while (has_data) {
+ if (!ReadMmapEventDataForFd(event_fd, selection.event_attr,
+ &has_data)) {
+ return false;
+ }
}
}
}
@@ -303,9 +402,9 @@ bool EventSelectionSet::ReadMmapEventData() {
return true;
}
-bool EventSelectionSet::ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd,
- const perf_event_attr& attr,
- bool* has_data) {
+bool EventSelectionSet::ReadMmapEventDataForFd(
+ std::unique_ptr<EventFd>& event_fd, const perf_event_attr& attr,
+ bool* has_data) {
*has_data = false;
while (true) {
char* data;
@@ -313,7 +412,8 @@ bool EventSelectionSet::ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_f
if (size == 0) {
break;
}
- std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr, data, size);
+ std::vector<std::unique_ptr<Record>> records =
+ ReadRecordsFromBuffer(attr, data, size);
record_cache_->Push(std::move(records));
std::unique_ptr<Record> r = record_cache_->Pop();
while (r != nullptr) {
@@ -336,25 +436,3 @@ bool EventSelectionSet::FinishReadMmapEventData() {
}
return true;
}
-
-EventSelectionSet::EventSelection* EventSelectionSet::FindSelectionByType(
- const EventTypeAndModifier& event_type_modifier) {
- for (auto& selection : selections_) {
- if (selection.event_type_modifier.name == event_type_modifier.name) {
- return &selection;
- }
- }
- return nullptr;
-}
-
-const perf_event_attr* EventSelectionSet::FindEventAttrByType(
- const EventTypeAndModifier& event_type_modifier) {
- EventSelection* selection = FindSelectionByType(event_type_modifier);
- return (selection != nullptr) ? &selection->event_attr : nullptr;
-}
-
-const std::vector<std::unique_ptr<EventFd>>* EventSelectionSet::FindEventFdsByType(
- const EventTypeAndModifier& event_type_modifier) {
- EventSelection* selection = FindSelectionByType(event_type_modifier);
- return (selection != nullptr) ? &selection->event_fds : nullptr;
-}
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index 3cfdb46b..d393a3b8 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -29,8 +29,17 @@
#include "perf_event.h"
#include "record.h"
+struct EventSelection {
+ uint32_t group_id;
+ uint32_t selection_id;
+ EventTypeAndModifier event_type_modifier;
+ perf_event_attr event_attr;
+ std::vector<std::unique_ptr<EventFd>> event_fds;
+};
+typedef std::vector<EventSelection> EventSelectionGroup;
+
struct CountersInfo {
- const EventTypeAndModifier* event_type;
+ const EventSelection* selection;
struct CounterInfo {
pid_t tid;
int cpu;
@@ -55,17 +64,22 @@ class EventSelectionSet {
EventSelectionSet() {
}
- bool Empty() const {
- return selections_.empty();
+ bool empty() const {
+ return groups_.empty();
+ }
+
+ const std::vector<EventSelectionGroup>& groups() {
+ return groups_;
}
- bool AddEventType(const EventTypeAndModifier& event_type_modifier);
+ bool AddEventType(const std::string& event_name);
+ bool AddEventGroup(const std::vector<std::string>& event_names);
void SetEnableOnExec(bool enable);
bool GetEnableOnExec();
void SampleIdAll();
- void SetSampleFreq(const EventTypeAndModifier& event_type_modifier, uint64_t sample_freq);
- void SetSamplePeriod(const EventTypeAndModifier& event_type_modifier, uint64_t sample_period);
+ void SetSampleFreq(const EventSelection& selection, uint64_t sample_freq);
+ void SetSamplePeriod(const EventSelection& selection, uint64_t sample_period);
bool SetBranchSampling(uint64_t branch_sample_type);
void EnableFpCallChainSampling();
bool EnableDwarfCallChainSampling(uint32_t dump_stack_size);
@@ -80,24 +94,15 @@ class EventSelectionSet {
bool ReadMmapEventData();
bool FinishReadMmapEventData();
- const perf_event_attr* FindEventAttrByType(const EventTypeAndModifier& event_type_modifier);
- const std::vector<std::unique_ptr<EventFd>>* FindEventFdsByType(
- const EventTypeAndModifier& event_type_modifier);
-
private:
+ bool BuildAndCheckEventSelection(const std::string& event_name,
+ EventSelection* selection);
void UnionSampleType();
bool OpenEventFiles(const std::vector<pid_t>& threads, const std::vector<int>& cpus);
bool ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd, const perf_event_attr& attr,
bool* has_data);
- struct EventSelection {
- EventTypeAndModifier event_type_modifier;
- perf_event_attr event_attr;
- std::vector<std::unique_ptr<EventFd>> event_fds;
- };
- EventSelection* FindSelectionByType(const EventTypeAndModifier& event_type_modifier);
-
- std::vector<EventSelection> selections_;
+ std::vector<EventSelectionGroup> groups_;
std::function<bool (Record*)> record_callback_;
std::unique_ptr<RecordCache> record_cache_;
diff --git a/simpleperf/tracing.cpp b/simpleperf/tracing.cpp
index 1757e05d..884a883c 100644
--- a/simpleperf/tracing.cpp
+++ b/simpleperf/tracing.cpp
@@ -396,18 +396,17 @@ const std::string& Tracing::GetKallsyms() const {
uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); }
-bool GetTracingData(const std::vector<EventTypeAndModifier>& event_types,
+bool GetTracingData(const std::vector<const EventType*>& event_types,
std::vector<char>* data) {
data->clear();
std::vector<TraceType> trace_types;
for (const auto& type : event_types) {
- if (type.event_type.type == PERF_TYPE_TRACEPOINT) {
- size_t pos = type.event_type.name.find(':');
- TraceType trace_type;
- trace_type.system = type.event_type.name.substr(0, pos);
- trace_type.name = type.event_type.name.substr(pos + 1);
- trace_types.push_back(trace_type);
- }
+ CHECK_EQ(PERF_TYPE_TRACEPOINT, type->type);
+ size_t pos = type->name.find(':');
+ TraceType trace_type;
+ trace_type.system = type->name.substr(0, pos);
+ trace_type.name = type->name.substr(pos + 1);
+ trace_types.push_back(trace_type);
}
TracingFile tracing_file;
if (!tracing_file.RecordHeaderFiles()) {
diff --git a/simpleperf/tracing.h b/simpleperf/tracing.h
index bd3c1bfe..9fd9534b 100644
--- a/simpleperf/tracing.h
+++ b/simpleperf/tracing.h
@@ -73,7 +73,7 @@ class Tracing {
std::vector<TracingFormat> tracing_formats_;
};
-bool GetTracingData(const std::vector<EventTypeAndModifier>& event_types,
+bool GetTracingData(const std::vector<const EventType*>& event_types,
std::vector<char>* data);
#endif // SIMPLE_PERF_TRACING_H_