diff options
-rw-r--r-- | simpleperf/Android.bp | 2 | ||||
-rw-r--r-- | simpleperf/MapRecordReader.cpp | 186 | ||||
-rw-r--r-- | simpleperf/MapRecordReader.h | 77 | ||||
-rw-r--r-- | simpleperf/MapRecordReader_test.cpp | 83 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 228 | ||||
-rw-r--r-- | simpleperf/record_file.h | 4 | ||||
-rw-r--r-- | simpleperf/test_util.cpp | 3 |
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; } - |