summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--simpleperf/Android.bp2
-rw-r--r--simpleperf/MapRecordReader.cpp186
-rw-r--r--simpleperf/MapRecordReader.h77
-rw-r--r--simpleperf/MapRecordReader_test.cpp83
-rw-r--r--simpleperf/cmd_record.cpp228
-rw-r--r--simpleperf/record_file.h4
-rw-r--r--simpleperf/test_util.cpp3
7 files changed, 458 insertions, 125 deletions
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp
index fd86b1af..f3062825 100644
--- a/simpleperf/Android.bp
+++ b/simpleperf/Android.bp
@@ -228,6 +228,7 @@ cc_defaults {
"event_selection_set.cpp",
"IOEventLoop.cpp",
"JITDebugReader.cpp",
+ "MapRecordReader.cpp",
"OfflineUnwinder.cpp",
"ProbeEvents.cpp",
"read_dex_file.cpp",
@@ -507,6 +508,7 @@ cc_defaults {
"cmd_trace_sched_test.cpp",
"environment_test.cpp",
"IOEventLoop_test.cpp",
+ "MapRecordReader_test.cpp",
"OfflineUnwinder_test.cpp",
"ProbeEvents_test.cpp",
"read_dex_file_test.cpp",
diff --git a/simpleperf/MapRecordReader.cpp b/simpleperf/MapRecordReader.cpp
new file mode 100644
index 00000000..e141e033
--- /dev/null
+++ b/simpleperf/MapRecordReader.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MapRecordReader.h"
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <vector>
+
+#include <android-base/strings.h>
+
+#include "environment.h"
+
+namespace simpleperf {
+
+bool MapRecordReader::ReadKernelMaps() {
+ KernelMmap kernel_mmap;
+ std::vector<KernelMmap> module_mmaps;
+ GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps);
+
+ MmapRecord mmap_record(attr_, true, UINT_MAX, 0, kernel_mmap.start_addr, kernel_mmap.len, 0,
+ kernel_mmap.filepath, event_id_);
+ if (!callback_(&mmap_record)) {
+ return false;
+ }
+ for (const auto& module_mmap : module_mmaps) {
+ MmapRecord mmap_record(attr_, true, UINT_MAX, 0, module_mmap.start_addr, module_mmap.len, 0,
+ module_mmap.filepath, event_id_);
+ if (!callback_(&mmap_record)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MapRecordReader::ReadProcessMaps(pid_t pid, uint64_t timestamp) {
+ std::vector<pid_t> tids = GetThreadsInProcess(pid);
+ return ReadProcessMaps(pid, std::unordered_set<pid_t>(tids.begin(), tids.end()), timestamp);
+}
+
+bool MapRecordReader::ReadProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids,
+ uint64_t timestamp) {
+ // Dump mmap records.
+ std::vector<ThreadMmap> thread_mmaps;
+ if (!GetThreadMmapsInProcess(pid, &thread_mmaps)) {
+ // The process may exit before we get its info.
+ return true;
+ }
+ for (const auto& map : thread_mmaps) {
+ if (!(map.prot & PROT_EXEC) && !keep_non_executable_maps_) {
+ continue;
+ }
+ Mmap2Record record(attr_, false, pid, pid, map.start_addr, map.len, map.pgoff, map.prot,
+ map.name, event_id_, timestamp);
+ if (!callback_(&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_, timestamp);
+ if (!callback_(&record)) {
+ 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_, timestamp);
+ if (!callback_(&comm_record)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+MapRecordThread::MapRecordThread(const MapRecordReader& map_record_reader)
+ : map_record_reader_(map_record_reader), fp_(nullptr, fclose) {
+ map_record_reader_.SetCallback([this](Record* r) { return WriteRecordToFile(r); });
+ tmpfile_ = ScopedTempFiles::CreateTempFile();
+ fp_.reset(fdopen(tmpfile_->release(), "r+"));
+ thread_ = std::thread([this]() { thread_result_ = RunThread(); });
+}
+
+MapRecordThread::~MapRecordThread() {
+ if (thread_.joinable()) {
+ early_stop_ = true;
+ thread_.join();
+ }
+}
+
+bool MapRecordThread::RunThread() {
+ if (!fp_) {
+ return false;
+ }
+ if (!map_record_reader_.ReadKernelMaps()) {
+ return false;
+ }
+ for (auto pid : GetAllProcesses()) {
+ if (early_stop_) {
+ return false;
+ }
+ if (!map_record_reader_.ReadProcessMaps(pid, 0)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MapRecordThread::WriteRecordToFile(Record* record) {
+ if (fwrite(record->Binary(), record->size(), 1, fp_.get()) != 1) {
+ PLOG(ERROR) << "failed to write map records to file";
+ return false;
+ }
+ return true;
+}
+
+bool MapRecordThread::Join() {
+ thread_.join();
+ if (!thread_result_) {
+ LOG(ERROR) << "map record thread failed";
+ }
+ return thread_result_;
+}
+
+bool MapRecordThread::ReadMapRecords(const std::function<bool(Record*)>& callback) {
+ off_t offset = ftello(fp_.get());
+ if (offset == -1) {
+ PLOG(ERROR) << "ftello() failed";
+ return false;
+ }
+ uint64_t file_size = static_cast<uint64_t>(offset);
+ if (fseek(fp_.get(), 0, SEEK_SET) != 0) {
+ PLOG(ERROR) << "fseek() failed";
+ return false;
+ }
+ uint64_t nread = 0;
+ std::vector<char> buffer(1024);
+ while (nread < file_size) {
+ if (fread(buffer.data(), Record::header_size(), 1, fp_.get()) != 1) {
+ PLOG(ERROR) << "fread() failed";
+ return false;
+ }
+ RecordHeader header(buffer.data());
+ if (buffer.size() < header.size) {
+ buffer.resize(header.size);
+ }
+ if (fread(buffer.data() + Record::header_size(), header.size - Record::header_size(), 1,
+ fp_.get()) != 1) {
+ PLOG(ERROR) << "fread() failed";
+ return false;
+ }
+ auto r = ReadRecordFromBuffer(map_record_reader_.Attr(), header.type, buffer.data());
+ CHECK(r);
+ if (!callback(r.get())) {
+ return false;
+ }
+ nread += header.size;
+ }
+ return true;
+}
+
+} // namespace simpleperf
diff --git a/simpleperf/MapRecordReader.h b/simpleperf/MapRecordReader.h
new file mode 100644
index 00000000..84269d0f
--- /dev/null
+++ b/simpleperf/MapRecordReader.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <unordered_set>
+#include <vector>
+
+#include "event_attr.h"
+#include "record.h"
+
+namespace simpleperf {
+
+class MapRecordReader {
+ public:
+ MapRecordReader(const perf_event_attr& attr, uint64_t event_id, bool keep_non_executable_maps)
+ : attr_(attr), event_id_(event_id), keep_non_executable_maps_(keep_non_executable_maps) {}
+
+ const perf_event_attr& Attr() { return attr_; }
+ void SetCallback(const std::function<bool(Record*)>& callback) { callback_ = callback; }
+ bool ReadKernelMaps();
+ // Read process maps and all thread names in a process.
+ bool ReadProcessMaps(pid_t pid, uint64_t timestamp);
+ // Read process maps and selected thread names in a process.
+ bool ReadProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids, uint64_t timestamp);
+
+ private:
+ const perf_event_attr& attr_;
+ const uint64_t event_id_;
+ const bool keep_non_executable_maps_;
+ std::function<bool(Record*)> callback_;
+};
+
+// Create a thread for reading maps while recording. The maps are stored in a temporary file, and
+// read back after recording.
+class MapRecordThread {
+ public:
+ MapRecordThread(const MapRecordReader& map_record_reader);
+ ~MapRecordThread();
+
+ bool Join();
+ bool ReadMapRecords(const std::function<bool(Record*)>& callback);
+
+ private:
+ // functions running in the map record thread
+ bool RunThread();
+ bool WriteRecordToFile(Record* record);
+
+ MapRecordReader map_record_reader_;
+ std::unique_ptr<TemporaryFile> tmpfile_;
+ std::unique_ptr<FILE, decltype(&fclose)> fp_;
+ std::thread thread_;
+ std::atomic<bool> early_stop_ = false;
+ std::atomic<bool> thread_result_ = false;
+};
+
+} // namespace simpleperf
diff --git a/simpleperf/MapRecordReader_test.cpp b/simpleperf/MapRecordReader_test.cpp
new file mode 100644
index 00000000..caab200d
--- /dev/null
+++ b/simpleperf/MapRecordReader_test.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MapRecordReader.h"
+
+#include <optional>
+
+#include <gtest/gtest.h>
+
+#include "environment.h"
+#include "event_attr.h"
+#include "event_type.h"
+
+using namespace simpleperf;
+
+class MapRecordReaderTest : public ::testing::Test {
+ protected:
+ bool CreateMapRecordReader() {
+ const EventType* event_type = FindEventTypeByName("cpu-clock");
+ if (event_type == nullptr) {
+ return false;
+ }
+ attr_ = CreateDefaultPerfEventAttr(*event_type);
+ reader_.emplace(attr_, 0, true);
+ reader_->SetCallback([this](Record* r) { return CountRecord(r); });
+ return true;
+ }
+
+ bool CountRecord(Record* r) {
+ if (r->type() == PERF_RECORD_MMAP || r->type() == PERF_RECORD_MMAP2) {
+ map_record_count_++;
+ } else if (r->type() == PERF_RECORD_COMM) {
+ comm_record_count_++;
+ }
+ return true;
+ }
+
+ perf_event_attr attr_;
+ std::optional<MapRecordReader> reader_;
+ size_t map_record_count_ = 0;
+ size_t comm_record_count_ = 0;
+};
+
+TEST_F(MapRecordReaderTest, ReadKernelMaps) {
+ ASSERT_TRUE(CreateMapRecordReader());
+ ASSERT_TRUE(reader_->ReadKernelMaps());
+ ASSERT_GT(map_record_count_, 0);
+}
+
+TEST_F(MapRecordReaderTest, ReadProcessMaps) {
+ ASSERT_TRUE(CreateMapRecordReader());
+ ASSERT_TRUE(reader_->ReadProcessMaps(getpid(), 0));
+ ASSERT_GT(map_record_count_, 0);
+ ASSERT_GT(comm_record_count_, 0);
+}
+
+TEST_F(MapRecordReaderTest, MapRecordThread) {
+#ifdef __ANDROID__
+ std::string tmpdir = "/data/local/tmp";
+#else
+ std::string tmpdir = "/tmp";
+#endif
+ ScopedTempFiles scoped_temp_files(tmpdir);
+ ASSERT_TRUE(CreateMapRecordReader());
+ MapRecordThread thread(*reader_);
+ ASSERT_TRUE(thread.Join());
+ ASSERT_TRUE(thread.ReadMapRecords([this](Record* r) { return CountRecord(r); }));
+ ASSERT_GT(map_record_count_, 0);
+ ASSERT_GT(comm_record_count_, 0);
+}
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.
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index 7244386d..485ba9f0 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -63,6 +63,7 @@ class RecordFileWriter {
bool WriteAttrSection(const std::vector<EventAttrWithId>& attr_ids);
bool WriteRecord(const Record& record);
+ bool WriteData(const void* buf, size_t len);
uint64_t GetDataSectionSize() const { return data_section_size_; }
bool ReadDataSection(const std::function<void(const Record*)>& callback);
@@ -87,7 +88,6 @@ class RecordFileWriter {
std::vector<std::string>* hit_kernel_modules,
std::vector<std::string>* hit_user_files);
bool WriteFileHeader();
- bool WriteData(const void* buf, size_t len);
bool Write(const void* buf, size_t len);
bool Read(void* buf, size_t len);
bool GetFilePos(uint64_t* file_pos);
@@ -145,6 +145,7 @@ class RecordFileReader {
// If sorted is true, sort records before passing them to callback function.
bool ReadDataSection(const std::function<bool(std::unique_ptr<Record>)>& callback);
+ bool ReadAtOffset(uint64_t offset, void* buf, size_t len);
// Read next record. If read successfully, set [record] and return true.
// If there is no more records, set [record] to nullptr and return true.
@@ -186,7 +187,6 @@ class RecordFileReader {
void UseRecordingEnvironment();
std::unique_ptr<Record> ReadRecord();
bool Read(void* buf, size_t len);
- bool ReadAtOffset(uint64_t offset, void* buf, size_t len);
void ProcessEventIdRecord(const EventIdRecord& r);
bool BuildAuxDataLocation();
diff --git a/simpleperf/test_util.cpp b/simpleperf/test_util.cpp
index d1b360a4..c2a21960 100644
--- a/simpleperf/test_util.cpp
+++ b/simpleperf/test_util.cpp
@@ -17,8 +17,8 @@
#include <stdio.h>
-#include <android-base/properties.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include "event_attr.h"
#include "event_fd.h"
@@ -133,4 +133,3 @@ bool HasTracepointEvents() {
}
return has_tracepoint_events == 1;
}
-