" --call-graph fp | dwarf[,<dump_stack_size>]\n"
" Enable call graph recording. Use frame pointer or dwarf as the\n"
" method to parse call graph in stack. Default is dwarf,8192.\n"
- " -e event[:modifier]\n"
- " Select the event to sample. Use `simpleperf list` to find\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\n"
" how the event should be monitored. Possible modifiers are:\n"
" u - monitor user space events only\n"
private:
bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
- bool SetMeasuredEventType(const std::string& event_type_name);
+ bool AddMeasuredEventType(const std::string& event_type_name);
bool SetEventSelection();
+ bool CreateRecordFile();
bool WriteData(const char* data, size_t size);
bool DumpKernelAndModuleMmaps();
bool DumpThreadCommAndMmaps(bool all_threads, const std::vector<pid_t>& selected_threads);
uint32_t dump_stack_size_in_dwarf_sampling_;
bool child_inherit_;
std::vector<pid_t> monitored_threads_;
- std::unique_ptr<EventTypeAndModifier> measured_event_type_modifier_;
+ std::vector<EventTypeAndModifier> measured_event_types_;
EventSelectionSet event_selection_set_;
// mmap pages used by each perf event file, should be power of 2.
if (!ParseOptions(args, &workload_args)) {
return false;
}
- if (measured_event_type_modifier_ == nullptr) {
- if (!SetMeasuredEventType(default_measured_event_type)) {
+ if (measured_event_types_.empty()) {
+ if (!AddMeasuredEventType(default_measured_event_type)) {
return false;
}
}
std::vector<pollfd> pollfds;
event_selection_set_.PreparePollForEventFiles(&pollfds);
- // 4. Open record file writer, and dump kernel/modules/threads mmap information.
- record_file_writer_ = RecordFileWriter::CreateInstance(
- record_filename_, event_selection_set_.FindEventAttrByType(*measured_event_type_modifier_),
- event_selection_set_.FindEventFdsByType(*measured_event_type_modifier_));
- if (record_file_writer_ == nullptr) {
- return false;
- }
- if (!DumpKernelAndModuleMmaps()) {
- return false;
- }
- if (!DumpThreadCommAndMmaps(system_wide_collection_, monitored_threads_)) {
+ // 4. Create perf.data.
+ if (!CreateRecordFile()) {
return false;
}
if (!NextArgumentOrError(args, &i)) {
return false;
}
- if (!SetMeasuredEventType(args[i])) {
- return false;
+ std::vector<std::string> event_types = android::base::Split(args[i], ",");
+ for (auto& event_type : event_types) {
+ if (!AddMeasuredEventType(event_type)) {
+ return false;
+ }
}
} else if (args[i] == "-f" || args[i] == "-F") {
if (!NextArgumentOrError(args, &i)) {
return true;
}
-bool RecordCommand::SetMeasuredEventType(const std::string& event_type_name) {
+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;
}
- measured_event_type_modifier_ = std::move(event_type_modifier);
+ measured_event_types_.push_back(*event_type_modifier);
return true;
}
bool RecordCommand::SetEventSelection() {
- if (!event_selection_set_.AddEventType(*measured_event_type_modifier_)) {
- return false;
+ for (auto& event_type : measured_event_types_) {
+ if (!event_selection_set_.AddEventType(event_type)) {
+ return false;
+ }
}
if (use_sample_freq_) {
event_selection_set_.SetSampleFreq(sample_freq_);
return true;
}
+bool RecordCommand::CreateRecordFile() {
+ record_file_writer_ = RecordFileWriter::CreateInstance(record_filename_);
+ if (record_file_writer_ == nullptr) {
+ return false;
+ }
+
+ 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);
+ }
+ if (!record_file_writer_->WriteAttrSection(attr_ids)) {
+ return false;
+ }
+
+ if (!DumpKernelAndModuleMmaps()) {
+ return false;
+ }
+ if (!DumpThreadCommAndMmaps(system_wide_collection_, monitored_threads_)) {
+ return false;
+ }
+ return true;
+}
+
bool RecordCommand::WriteData(const char* data, size_t size) {
return record_file_writer_->WriteData(data, size);
}
if (!GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps)) {
return false;
}
- const perf_event_attr& attr =
- event_selection_set_.FindEventAttrByType(*measured_event_type_modifier_);
- MmapRecord mmap_record = CreateMmapRecord(attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
+ const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
+ CHECK(attr != nullptr);
+ MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
kernel_mmap.len, kernel_mmap.pgoff, kernel_mmap.name);
if (!record_file_writer_->WriteData(mmap_record.BinaryFormat())) {
return false;
if (filename.empty()) {
filename = "[" + module_mmap.name + "]";
}
- MmapRecord mmap_record = CreateMmapRecord(attr, true, UINT_MAX, 0, module_mmap.start_addr,
+ MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, module_mmap.start_addr,
module_mmap.len, 0, filename);
if (!record_file_writer_->WriteData(mmap_record.BinaryFormat())) {
return false;
}
}
- const perf_event_attr& attr =
- event_selection_set_.FindEventAttrByType(*measured_event_type_modifier_);
+ const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
+ CHECK(attr != nullptr);
// Dump processes.
for (auto& thread : thread_comms) {
if (!all_threads && dump_processes.find(thread.pid) == dump_processes.end()) {
continue;
}
- CommRecord record = CreateCommRecord(attr, thread.pid, thread.tid, thread.comm);
+ CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
if (!record_file_writer_->WriteData(record.BinaryFormat())) {
return false;
}
continue; // No need to dump non-executable mmap info.
}
MmapRecord record =
- CreateMmapRecord(attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
+ CreateMmapRecord(*attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
thread_mmap.len, thread_mmap.pgoff, thread_mmap.name);
if (!record_file_writer_->WriteData(record.BinaryFormat())) {
return false;
if (!all_threads && dump_threads.find(thread.tid) == dump_threads.end()) {
continue;
}
- ForkRecord fork_record = CreateForkRecord(attr, thread.pid, thread.tid, thread.pid, thread.pid);
+ ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid, thread.pid);
if (!record_file_writer_->WriteData(fork_record.BinaryFormat())) {
return false;
}
- CommRecord comm_record = CreateCommRecord(attr, thread.pid, thread.tid, thread.comm);
+ CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
if (!record_file_writer_->WriteData(comm_record.BinaryFormat())) {
return false;
}
TEST(record_cmd, no_monitored_threads) {
ASSERT_FALSE(RecordCmd()->Run({""}));
}
+
+TEST(record_cmd, more_than_one_event_types) {
+ ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-cycles,cpu-clock", "sleep", "1"}));
+ ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-cycles", "-e", "cpu-clock", "sleep", "1"}));
+}
return false;
}
selections_.push_back(std::move(selection));
+ UnionSampleType();
return true;
}
+// 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 (auto& selection : selections_) {
+ selection.event_attr.sample_type = sample_type;
+ }
+}
+
void EventSelectionSet::SetEnableOnExec(bool enable) {
for (auto& selection : selections_) {
selection.event_attr.enable_on_exec = (enable ? 1 : 0);
return nullptr;
}
-const perf_event_attr& EventSelectionSet::FindEventAttrByType(
+const perf_event_attr* EventSelectionSet::FindEventAttrByType(
const EventTypeAndModifier& event_type_modifier) {
- return FindSelectionByType(event_type_modifier)->event_attr;
+ EventSelection* selection = FindSelectionByType(event_type_modifier);
+ return (selection != nullptr) ? &selection->event_attr : nullptr;
}
-const std::vector<std::unique_ptr<EventFd>>& EventSelectionSet::FindEventFdsByType(
+const std::vector<std::unique_ptr<EventFd>>* EventSelectionSet::FindEventFdsByType(
const EventTypeAndModifier& event_type_modifier) {
- return FindSelectionByType(event_type_modifier)->event_fds;
+ EventSelection* selection = FindSelectionByType(event_type_modifier);
+ return (selection != nullptr) ? &selection->event_fds : nullptr;
}
bool MmapEventFiles(size_t mmap_pages);
bool ReadMmapEventData(std::function<bool(const char*, size_t)> callback);
- const perf_event_attr& FindEventAttrByType(const EventTypeAndModifier& event_type_modifier);
- const std::vector<std::unique_ptr<EventFd>>& FindEventFdsByType(
+ const perf_event_attr* FindEventAttrByType(const EventTypeAndModifier& event_type_modifier);
+ const std::vector<std::unique_ptr<EventFd>>* FindEventFdsByType(
const EventTypeAndModifier& event_type_modifier);
private:
+ void UnionSampleType();
bool OpenEventFiles(const std::vector<pid_t>& threads, const std::vector<int>& cpus);
struct EventSelection {
#include "record.h"
#include "record_file_format.h"
-class EventFd;
+struct AttrWithId {
+ const perf_event_attr* attr;
+ std::vector<uint64_t> ids;
+};
// RecordFileWriter writes to a perf record file, like perf.data.
class RecordFileWriter {
public:
- static std::unique_ptr<RecordFileWriter> CreateInstance(
- const std::string& filename, const perf_event_attr& event_attr,
- const std::vector<std::unique_ptr<EventFd>>& event_fds);
+ static std::unique_ptr<RecordFileWriter> CreateInstance(const std::string& filename);
~RecordFileWriter();
+ bool WriteAttrSection(const std::vector<AttrWithId>& attr_ids);
bool WriteData(const void* buf, size_t len);
bool WriteData(const std::vector<char>& data) {
private:
RecordFileWriter(const std::string& filename, FILE* fp);
- bool WriteAttrSection(const perf_event_attr& event_attr,
- const std::vector<std::unique_ptr<EventFd>>& event_fds);
void GetHitModulesInBuffer(const char* p, const char* end,
std::vector<std::string>* hit_kernel_modules,
std::vector<std::string>* hit_user_files);
#include <string.h>
#include "environment.h"
#include "event_attr.h"
-#include "event_fd.h"
#include "event_type.h"
#include "record.h"
#include "record_file.h"
class RecordFileTest : public ::testing::Test {
protected:
virtual void SetUp() {
- filename = "temporary.record_file";
- std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
+ filename_ = "temporary.record_file";
+ }
+
+ void AddEventType(const std::string& event_type_str) {
+ std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_str);
ASSERT_TRUE(event_type_modifier != nullptr);
- event_attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
- event_attr.sample_id_all = 1;
- event_attr.sample_type |= PERF_SAMPLE_TIME;
- std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(event_attr, getpid(), -1);
- ASSERT_TRUE(event_fd != nullptr);
- event_fds.push_back(std::move(event_fd));
+ perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
+ attrs_.push_back(attr);
+ AttrWithId attr_id;
+ attr_id.attr = &attrs_[attrs_.size() - 1];
+ attr_id.ids.push_back(attrs_.size()); // Fake id.
+ attr_ids_.push_back(attr_id);
}
- std::string filename;
- perf_event_attr event_attr;
- std::vector<std::unique_ptr<EventFd>> event_fds;
+ std::string filename_;
+ std::vector<perf_event_attr> attrs_;
+ std::vector<AttrWithId> attr_ids_;
};
TEST_F(RecordFileTest, smoke) {
// Write to a record file.
- std::unique_ptr<RecordFileWriter> writer =
- RecordFileWriter::CreateInstance(filename, event_attr, event_fds);
+ std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename_);
ASSERT_TRUE(writer != nullptr);
+ // Write attr section.
+ AddEventType("cpu-cycles");
+ ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
+
// Write data section.
MmapRecord mmap_record =
- CreateMmapRecord(event_attr, true, 1, 1, 0x1000, 0x2000, 0x3000, "mmap_record_example");
+ CreateMmapRecord(attrs_[0], true, 1, 1, 0x1000, 0x2000, 0x3000, "mmap_record_example");
ASSERT_TRUE(writer->WriteData(mmap_record.BinaryFormat()));
// Check data section that has been written.
ASSERT_TRUE(writer->Close());
// Read from a record file.
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename);
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename_);
ASSERT_TRUE(reader != nullptr);
const FileHeader* file_header = reader->FileHeader();
ASSERT_TRUE(file_header != nullptr);
- std::vector<const FileAttr*> attrs = reader->AttrSection();
- ASSERT_EQ(1u, attrs.size());
- ASSERT_EQ(0, memcmp(&attrs[0]->attr, &event_attr, sizeof(perf_event_attr)));
- std::vector<uint64_t> ids = reader->IdsForAttr(attrs[0]);
- ASSERT_EQ(1u, ids.size());
+ std::vector<const FileAttr*> file_attrs = reader->AttrSection();
+ ASSERT_EQ(1u, file_attrs.size());
+ ASSERT_EQ(0, memcmp(&file_attrs[0]->attr, attr_ids_[0].attr, sizeof(perf_event_attr)));
+ std::vector<uint64_t> ids = reader->IdsForAttr(file_attrs[0]);
+ ASSERT_EQ(ids, attr_ids_[0].ids);
// Read and check data section.
records = reader->DataSection();
}
TEST_F(RecordFileTest, records_sorted_by_time) {
- // Write to a record file;
- std::unique_ptr<RecordFileWriter> writer =
- RecordFileWriter::CreateInstance(filename, event_attr, event_fds);
+ // Write to a record file.
+ std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename_);
ASSERT_TRUE(writer != nullptr);
+ // Write attr section.
+ AddEventType("cpu-cycles");
+ attrs_[0].sample_id_all = 1;
+ attrs_[0].sample_type |= PERF_SAMPLE_TIME;
+ ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
+
// Write data section.
- MmapRecord r1 = CreateMmapRecord(event_attr, true, 1, 1, 0x100, 0x2000, 0x3000, "mmap_record1");
+ MmapRecord r1 = CreateMmapRecord(attrs_[0], true, 1, 1, 0x100, 0x2000, 0x3000, "mmap_record1");
MmapRecord r2 = r1;
MmapRecord r3 = r1;
r1.sample_id.time_data.time = 2;
ASSERT_TRUE(writer->Close());
// Read from a record file.
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename);
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename_);
ASSERT_TRUE(reader != nullptr);
std::vector<std::unique_ptr<Record>> records = reader->DataSection();
ASSERT_EQ(3u, records.size());
ASSERT_TRUE(reader->Close());
}
+
+TEST_F(RecordFileTest, record_more_than_one_attr) {
+ // Write to a record file.
+ std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename_);
+ ASSERT_TRUE(writer != nullptr);
+
+ // Write attr section.
+ AddEventType("cpu-cycles");
+ AddEventType("cpu-clock");
+ AddEventType("task-clock");
+ ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
+
+ ASSERT_TRUE(writer->Close());
+
+ // Read from a record file.
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(filename_);
+ ASSERT_TRUE(reader != nullptr);
+ std::vector<const FileAttr*> file_attrs = reader->AttrSection();
+ ASSERT_EQ(3u, file_attrs.size());
+ for (size_t i = 0; i < file_attrs.size(); ++i) {
+ ASSERT_EQ(0, memcmp(&file_attrs[i]->attr, attr_ids_[i].attr, sizeof(perf_event_attr)));
+ std::vector<uint64_t> ids = reader->IdsForAttr(file_attrs[i]);
+ ASSERT_EQ(ids, attr_ids_[i].ids);
+ }
+}
#include <base/logging.h>
-#include "event_fd.h"
#include "perf_event.h"
#include "record.h"
#include "utils.h"
using namespace PerfFileFormat;
-std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(
- const std::string& filename, const perf_event_attr& event_attr,
- const std::vector<std::unique_ptr<EventFd>>& event_fds) {
+std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(const std::string& filename) {
// Remove old perf.data to avoid file ownership problems.
if (!RemovePossibleFile(filename)) {
return nullptr;
return nullptr;
}
- auto writer = std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp));
- if (!writer->WriteAttrSection(event_attr, event_fds)) {
- return nullptr;
- }
- return writer;
+ return std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp));
}
RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp)
}
}
-bool RecordFileWriter::WriteAttrSection(const perf_event_attr& event_attr,
- const std::vector<std::unique_ptr<EventFd>>& event_fds) {
+bool RecordFileWriter::WriteAttrSection(const std::vector<AttrWithId>& attr_ids) {
+ if (attr_ids.empty()) {
+ return false;
+ }
+
// Skip file header part.
if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) {
return false;
}
// Write id section.
- std::vector<uint64_t> ids;
- for (auto& event_fd : event_fds) {
- ids.push_back(event_fd->Id());
- }
long id_section_offset = ftell(record_fp_);
if (id_section_offset == -1) {
return false;
}
- if (!Write(ids.data(), ids.size() * sizeof(uint64_t))) {
- return false;
+ for (auto& attr_id : attr_ids) {
+ if (!Write(attr_id.ids.data(), attr_id.ids.size() * sizeof(uint64_t))) {
+ return false;
+ }
}
// Write attr section.
- FileAttr attr;
- attr.attr = event_attr;
- attr.ids.offset = id_section_offset;
- attr.ids.size = ids.size() * sizeof(uint64_t);
-
long attr_section_offset = ftell(record_fp_);
if (attr_section_offset == -1) {
return false;
}
- if (!Write(&attr, sizeof(attr))) {
- return false;
+ for (auto& attr_id : attr_ids) {
+ FileAttr file_attr;
+ file_attr.attr = *attr_id.attr;
+ file_attr.ids.offset = id_section_offset;
+ file_attr.ids.size = attr_id.ids.size() * sizeof(uint64_t);
+ id_section_offset += file_attr.ids.size;
+ if (!Write(&file_attr, sizeof(file_attr))) {
+ return false;
+ }
}
long data_section_offset = ftell(record_fp_);
}
attr_section_offset_ = attr_section_offset;
- attr_section_size_ = sizeof(attr);
+ attr_section_size_ = data_section_offset - attr_section_offset;
data_section_offset_ = data_section_offset;
// Save event_attr for use when reading records.
- event_attr_ = event_attr;
+ event_attr_ = *attr_ids[0].attr;
return true;
}