summaryrefslogtreecommitdiff
path: root/simpleperf/cmd_record.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'simpleperf/cmd_record.cpp')
-rw-r--r--simpleperf/cmd_record.cpp228
1 files changed, 107 insertions, 121 deletions
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 43c906c9..2225ffe1 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -22,6 +22,7 @@
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
+#include <optional>
#include <set>
#include <string>
#include <unordered_map>
@@ -43,6 +44,7 @@
#include "ETMRecorder.h"
#include "IOEventLoop.h"
#include "JITDebugReader.h"
+#include "MapRecordReader.h"
#include "OfflineUnwinder.h"
#include "ProbeEvents.h"
#include "cmd_record_impl.h"
@@ -64,6 +66,8 @@ using android::base::ParseUint;
using android::base::Realpath;
using namespace simpleperf;
+namespace {
+
static std::string default_measured_event_type = "cpu-cycles";
static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
@@ -318,16 +322,17 @@ class RecordCommand : public Command {
bool PrepareRecording(Workload* workload);
bool DoRecording(Workload* workload);
bool PostProcessRecording(const std::vector<std::string>& args);
+ // pre recording functions
bool TraceOffCpu();
bool SetEventSelectionFlags();
bool CreateAndInitRecordFile();
std::unique_ptr<RecordFileWriter> CreateRecordFile(const std::string& filename);
bool DumpKernelSymbol();
bool DumpTracingData();
- bool DumpKernelMaps();
- bool DumpUserSpaceMaps();
- bool DumpProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids);
+ bool DumpMaps();
bool DumpAuxTraceInfo();
+
+ // recording functions
bool ProcessRecord(Record* record);
bool ShouldOmitRecord(Record* record);
bool DumpMapsForRecord(Record* record);
@@ -336,9 +341,12 @@ class RecordCommand : public Command {
bool SaveRecordWithoutUnwinding(Record* record);
bool ProcessJITDebugInfo(const std::vector<JITDebugInfo>& debug_info, bool sync_kernel_records);
bool ProcessControlCmd(IOEventLoop* loop);
-
void UpdateRecord(Record* record);
bool UnwindRecord(SampleRecord& r);
+
+ // post recording functions
+ std::unique_ptr<RecordFileReader> MoveRecordFile(const std::string& old_filename);
+ bool MergeMapRecords();
bool PostUnwindRecords();
bool JoinCallChains();
bool DumpAdditionalFeatures(const std::vector<std::string>& args);
@@ -399,6 +407,9 @@ class RecordCommand : public Command {
// In system wide recording, record if we have dumped map info for a process.
std::unordered_set<pid_t> dumped_processes_;
bool exclude_perf_ = false;
+
+ std::optional<MapRecordReader> map_record_reader_;
+ std::optional<MapRecordThread> map_record_thread_;
};
bool RecordCommand::Run(const std::vector<std::string>& args) {
@@ -670,19 +681,26 @@ static bool WriteRecordDataToOutFd(const std::string& in_filename,
}
bool RecordCommand::PostProcessRecording(const std::vector<std::string>& args) {
- // 1. Post unwind dwarf callchain.
+ // 1. Merge map records dumped while recording by map record thread.
+ if (map_record_thread_) {
+ if (!map_record_thread_->Join() || !MergeMapRecords()) {
+ return false;
+ }
+ }
+
+ // 2. Post unwind dwarf callchain.
if (unwind_dwarf_callchain_ && post_unwind_) {
if (!PostUnwindRecords()) {
return false;
}
}
- // 2. Optionally join Callchains.
+ // 3. Optionally join Callchains.
if (callchain_joiner_) {
JoinCallChains();
}
- // 3. Dump additional features, and close record file.
+ // 4. Dump additional features, and close record file.
if (!DumpAdditionalFeatures(args)) {
return false;
}
@@ -1152,9 +1170,12 @@ bool RecordCommand::CreateAndInitRecordFile() {
return false;
}
// Use first perf_event_attr and first event id to dump mmap and comm records.
- dumping_attr_id_ = event_selection_set_.GetEventAttrWithId()[0];
- return DumpKernelSymbol() && DumpTracingData() && DumpKernelMaps() && DumpUserSpaceMaps() &&
- DumpAuxTraceInfo();
+ EventAttrWithId dumping_attr_id = event_selection_set_.GetEventAttrWithId()[0];
+ map_record_reader_.emplace(*dumping_attr_id.attr, dumping_attr_id.ids[0],
+ event_selection_set_.RecordNotExecutableMaps());
+ map_record_reader_->SetCallback([this](Record* r) { return ProcessRecord(r); });
+
+ return DumpKernelSymbol() && DumpTracingData() && DumpMaps() && DumpAuxTraceInfo();
}
std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(const std::string& filename) {
@@ -1202,104 +1223,41 @@ bool RecordCommand::DumpTracingData() {
return true;
}
-bool RecordCommand::DumpKernelMaps() {
- KernelMmap kernel_mmap;
- std::vector<KernelMmap> module_mmaps;
- GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps);
-
- MmapRecord mmap_record(*dumping_attr_id_.attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
- kernel_mmap.len, 0, kernel_mmap.filepath, dumping_attr_id_.ids[0]);
- if (!ProcessRecord(&mmap_record)) {
- return false;
- }
- for (auto& module_mmap : module_mmaps) {
- MmapRecord mmap_record(*dumping_attr_id_.attr, true, UINT_MAX, 0, module_mmap.start_addr,
- module_mmap.len, 0, module_mmap.filepath, dumping_attr_id_.ids[0]);
- if (!ProcessRecord(&mmap_record)) {
- return false;
+bool RecordCommand::DumpMaps() {
+ if (system_wide_collection_) {
+ // For system wide recording:
+ // If not aux tracing, only dump kernel maps. Maps of a process is dumped when needed (the
+ // first time a sample hits that process).
+ // If aux tracing, we don't know which maps will be needed, so dump all process maps. To
+ // reduce pre recording time, we dump process maps in map record thread while recording.
+ if (event_selection_set_.HasAuxTrace()) {
+ map_record_thread_.emplace(*map_record_reader_);
+ return true;
}
+ return map_record_reader_->ReadKernelMaps();
}
- return true;
-}
-
-bool RecordCommand::DumpUserSpaceMaps() {
- // For system_wide profiling:
- // If no aux tracing, maps of a process is dumped when needed (first time a sample hits
- // that process).
- // If aux tracing, we don't know which maps will be needed, so dump all process maps.
- if (system_wide_collection_ && !event_selection_set_.HasAuxTrace()) {
- return true;
+ if (!map_record_reader_->ReadKernelMaps()) {
+ return false;
}
// Map from process id to a set of thread ids in that process.
std::unordered_map<pid_t, std::unordered_set<pid_t>> process_map;
- if (system_wide_collection_) {
- for (auto pid : GetAllProcesses()) {
- process_map[pid] = std::unordered_set<pid_t>();
- }
- } else {
- for (pid_t pid : event_selection_set_.GetMonitoredProcesses()) {
- std::vector<pid_t> tids = GetThreadsInProcess(pid);
- process_map[pid].insert(tids.begin(), tids.end());
- }
- for (pid_t tid : event_selection_set_.GetMonitoredThreads()) {
- pid_t pid;
- if (GetProcessForThread(tid, &pid)) {
- process_map[pid].insert(tid);
- }
- }
+ for (pid_t pid : event_selection_set_.GetMonitoredProcesses()) {
+ std::vector<pid_t> tids = GetThreadsInProcess(pid);
+ process_map[pid].insert(tids.begin(), tids.end());
}
-
- // Dump each process.
- for (auto& pair : process_map) {
- if (!DumpProcessMaps(pair.first, pair.second)) {
- return false;
+ for (pid_t tid : event_selection_set_.GetMonitoredThreads()) {
+ pid_t pid;
+ if (GetProcessForThread(tid, &pid)) {
+ process_map[pid].insert(tid);
}
}
- return true;
-}
-bool RecordCommand::DumpProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids) {
- // Dump mmap records.
- std::vector<ThreadMmap> thread_mmaps;
- if (!GetThreadMmapsInProcess(pid, &thread_mmaps)) {
- // The process may exit before we get its info.
- return true;
- }
- const perf_event_attr& attr = *dumping_attr_id_.attr;
- uint64_t event_id = dumping_attr_id_.ids[0];
- for (const auto& map : thread_mmaps) {
- if (!(map.prot & PROT_EXEC) && !event_selection_set_.RecordNotExecutableMaps()) {
- continue;
- }
- Mmap2Record record(attr, false, pid, pid, map.start_addr, map.len, map.pgoff, map.prot,
- map.name, event_id, last_record_timestamp_);
- if (!ProcessRecord(&record)) {
- return false;
- }
- }
- // Dump process name.
- std::string process_name = GetCompleteProcessName(pid);
- if (!process_name.empty()) {
- CommRecord record(attr, pid, pid, process_name, event_id, last_record_timestamp_);
- if (!ProcessRecord(&record)) {
+ // Dump each process.
+ for (const auto& [pid, tids] : process_map) {
+ if (!map_record_reader_->ReadProcessMaps(pid, tids, 0)) {
return false;
}
}
- // Dump thread info.
- for (const auto& tid : tids) {
- std::string name;
- if (tid != pid && GetThreadName(tid, &name)) {
- // If a thread name matches the suffix of its process name, probably the thread name
- // is stripped by TASK_COMM_LEN.
- if (android::base::EndsWith(process_name, name)) {
- name = process_name;
- }
- CommRecord comm_record(attr, pid, tid, name, event_id, last_record_timestamp_);
- if (!ProcessRecord(&comm_record)) {
- return false;
- }
- }
- }
return true;
}
@@ -1365,9 +1323,7 @@ bool RecordCommand::DumpMapsForRecord(Record* record) {
pid_t pid = static_cast<SampleRecord*>(record)->tid_data.pid;
if (dumped_processes_.find(pid) == dumped_processes_.end()) {
// Dump map info and all thread names for that process.
- std::vector<pid_t> tids = GetThreadsInProcess(pid);
- if (!tids.empty() &&
- !DumpProcessMaps(pid, std::unordered_set<pid_t>(tids.begin(), tids.end()))) {
+ if (!map_record_reader_->ReadProcessMaps(pid, last_record_timestamp_)) {
return false;
}
dumped_processes_.insert(pid);
@@ -1582,24 +1538,61 @@ bool RecordCommand::UnwindRecord(SampleRecord& r) {
return true;
}
-bool RecordCommand::PostUnwindRecords() {
- // 1. Move records from record_filename_ to a temporary file.
+std::unique_ptr<RecordFileReader> RecordCommand::MoveRecordFile(const std::string& old_filename) {
if (!record_file_writer_->Close()) {
- return false;
+ return nullptr;
}
record_file_writer_.reset();
- std::unique_ptr<TemporaryFile> tmp_file = ScopedTempFiles::CreateTempFile();
- if (!Workload::RunCmd({"mv", record_filename_, tmp_file->path})) {
- return false;
+ if (!Workload::RunCmd({"mv", record_filename_, old_filename})) {
+ return nullptr;
+ }
+ record_file_writer_ = CreateRecordFile(record_filename_);
+ if (!record_file_writer_) {
+ return nullptr;
}
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmp_file->path);
+ return RecordFileReader::CreateInstance(old_filename);
+}
+
+bool RecordCommand::MergeMapRecords() {
+ // 1. Move records from record_filename_ to a temporary file.
+ auto tmp_file = ScopedTempFiles::CreateTempFile();
+ auto reader = MoveRecordFile(tmp_file->path);
if (!reader) {
return false;
}
- // 2. Read records from the temporary file, and write unwound records back to record_filename_.
- record_file_writer_ = CreateRecordFile(record_filename_);
- if (!record_file_writer_) {
+ // 2. Copy map records from map record thread.
+ auto callback = [this](Record* r) {
+ UpdateRecord(r);
+ if (ShouldOmitRecord(r)) {
+ return true;
+ }
+ return record_file_writer_->WriteRecord(*r);
+ };
+ if (!map_record_thread_->ReadMapRecords(callback)) {
+ return false;
+ }
+
+ // 3. Copy data section from the old recording file.
+ std::vector<char> buf(64 * 1024);
+ uint64_t offset = reader->FileHeader().data.offset;
+ uint64_t left_size = reader->FileHeader().data.size;
+ while (left_size > 0) {
+ size_t nread = std::min<size_t>(left_size, buf.size());
+ if (!reader->ReadAtOffset(offset, buf.data(), nread) ||
+ !record_file_writer_->WriteData(buf.data(), nread)) {
+ return false;
+ }
+ offset += nread;
+ left_size -= nread;
+ }
+ return true;
+}
+
+bool RecordCommand::PostUnwindRecords() {
+ auto tmp_file = ScopedTempFiles::CreateTempFile();
+ auto reader = MoveRecordFile(tmp_file->path);
+ if (!reader) {
return false;
}
sample_record_count_ = 0;
@@ -1616,23 +1609,14 @@ bool RecordCommand::JoinCallChains() {
return false;
}
// 2. Move records from record_filename_ to a temporary file.
- if (!record_file_writer_->Close()) {
- return false;
- }
- record_file_writer_.reset();
- std::unique_ptr<TemporaryFile> tmp_file = ScopedTempFiles::CreateTempFile();
- if (!Workload::RunCmd({"mv", record_filename_, tmp_file->path})) {
+ auto tmp_file = ScopedTempFiles::CreateTempFile();
+ auto reader = MoveRecordFile(tmp_file->path);
+ if (!reader) {
return false;
}
// 3. Read records from the temporary file, and write record with joined call chains back
// to record_filename_.
- std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmp_file->path);
- record_file_writer_ = CreateRecordFile(record_filename_);
- if (!reader || !record_file_writer_) {
- return false;
- }
-
auto record_callback = [&](std::unique_ptr<Record> r) {
if (r->type() != PERF_RECORD_SAMPLE) {
return record_file_writer_->WriteRecord(*r);
@@ -1886,6 +1870,8 @@ void RecordCommand::CollectHitFileInfo(const SampleRecord& r) {
}
}
+} // namespace
+
namespace simpleperf {
// To reduce function length, not all format errors are checked.