diff options
-rw-r--r-- | simpleperf/SampleComparator.h | 4 | ||||
-rw-r--r-- | simpleperf/SampleDisplayer.h | 4 | ||||
-rw-r--r-- | simpleperf/cmd_kmem.cpp | 2 | ||||
-rw-r--r-- | simpleperf/cmd_report.cpp | 17 | ||||
-rw-r--r-- | simpleperf/cmd_report_sample.cpp | 21 | ||||
-rw-r--r-- | simpleperf/cmd_report_sample_test.cpp | 1 | ||||
-rw-r--r-- | simpleperf/sample_tree.h | 6 | ||||
-rw-r--r-- | simpleperf/sample_tree_test.cpp | 2 | ||||
-rwxr-xr-x | simpleperf/scripts/test.py | 1 | ||||
-rw-r--r-- | simpleperf/thread_tree.cpp | 46 | ||||
-rw-r--r-- | simpleperf/thread_tree.h | 5 | ||||
-rw-r--r-- | simpleperf/thread_tree_test.cpp | 15 |
12 files changed, 71 insertions, 53 deletions
diff --git a/simpleperf/SampleComparator.h b/simpleperf/SampleComparator.h index 77781811..465370a1 100644 --- a/simpleperf/SampleComparator.h +++ b/simpleperf/SampleComparator.h @@ -50,8 +50,8 @@ int Compare(const T& a, const T& b) { return strcmp(sample1->compare_part, sample2->compare_part); \ } -BUILD_COMPARE_VALUE_FUNCTION(ComparePid, thread->pid); -BUILD_COMPARE_VALUE_FUNCTION(CompareTid, thread->tid); +BUILD_COMPARE_VALUE_FUNCTION(ComparePid, pid); +BUILD_COMPARE_VALUE_FUNCTION(CompareTid, tid); BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareSampleCount, sample_count); BUILD_COMPARE_STRING_FUNCTION(CompareComm, thread_comm); BUILD_COMPARE_STRING_FUNCTION(CompareDso, map->dso->Path().c_str()); diff --git a/simpleperf/SampleDisplayer.h b/simpleperf/SampleDisplayer.h index 2dde02eb..3a59abe5 100644 --- a/simpleperf/SampleDisplayer.h +++ b/simpleperf/SampleDisplayer.h @@ -66,12 +66,12 @@ BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count); template <typename EntryT> std::string DisplayPid(const EntryT* sample) { - return android::base::StringPrintf("%d", sample->thread->pid); + return android::base::StringPrintf("%d", static_cast<int>(sample->pid)); } template <typename EntryT> std::string DisplayTid(const EntryT* sample) { - return android::base::StringPrintf("%d", sample->thread->tid); + return android::base::StringPrintf("%d", static_cast<int>(sample->tid)); } template <typename EntryT> diff --git a/simpleperf/cmd_kmem.cpp b/simpleperf/cmd_kmem.cpp index 5d58c394..453a2768 100644 --- a/simpleperf/cmd_kmem.cpp +++ b/simpleperf/cmd_kmem.cpp @@ -211,7 +211,7 @@ class SlabSampleTreeBuilder return nullptr; } - SlabSample* CreateCallChainSample( + SlabSample* CreateCallChainSample(const ThreadEntry*, const SlabSample* sample, uint64_t ip, bool in_kernel, const std::vector<SlabSample*>& callchain, const SlabAccumulateInfo& acc_info) override { diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp index e6f5dabb..f7aeb281 100644 --- a/simpleperf/cmd_report.cpp +++ b/simpleperf/cmd_report.cpp @@ -62,7 +62,8 @@ struct SampleEntry { // accumuated when appearing in other sample's callchain uint64_t accumulated_period; uint64_t sample_count; - const ThreadEntry* thread; + pid_t pid; + pid_t tid; const char* thread_comm; const MapEntry* map; const Symbol* symbol; @@ -78,7 +79,8 @@ struct SampleEntry { period(period), accumulated_period(accumulated_period), sample_count(sample_count), - thread(thread), + pid(thread->pid), + tid(thread->tid), thread_comm(thread->comm), map(map), symbol(symbol), @@ -183,11 +185,10 @@ class ReportCmdSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, uint64_ return InsertSample(std::move(sample)); } - SampleEntry* CreateCallChainSample(const SampleEntry* sample, uint64_t ip, - bool in_kernel, + SampleEntry* CreateCallChainSample(const ThreadEntry* thread, const SampleEntry* sample, + uint64_t ip, bool in_kernel, const std::vector<SampleEntry*>& callchain, const uint64_t& acc_info) override { - const ThreadEntry* thread = sample->thread; const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel); if (thread_tree_->IsUnknownDso(map->dso)) { // The unwinders can give wrong ip addresses, which can't map to a valid dso. Skip them. @@ -203,7 +204,7 @@ class ReportCmdSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, uint64_ } const ThreadEntry* GetThreadOfSample(SampleEntry* sample) override { - return sample->thread; + return thread_tree_->FindThreadOrNew(sample->pid, sample->tid); } uint64_t GetPeriodForCallChain(const uint64_t& acc_info) override { @@ -212,11 +213,11 @@ class ReportCmdSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, uint64_ bool FilterSample(const SampleEntry* sample) override { if (!pid_filter_.empty() && - pid_filter_.find(sample->thread->pid) == pid_filter_.end()) { + pid_filter_.find(sample->pid) == pid_filter_.end()) { return false; } if (!tid_filter_.empty() && - tid_filter_.find(sample->thread->tid) == tid_filter_.end()) { + tid_filter_.find(sample->tid) == tid_filter_.end()) { return false; } if (!comm_filter_.empty() && diff --git a/simpleperf/cmd_report_sample.cpp b/simpleperf/cmd_report_sample.cpp index d4b280cf..51793255 100644 --- a/simpleperf/cmd_report_sample.cpp +++ b/simpleperf/cmd_report_sample.cpp @@ -16,6 +16,7 @@ #include <inttypes.h> +#include <limits> #include <memory> #include <android-base/strings.h> @@ -112,6 +113,7 @@ class ReportSampleCommand : public Command { bool OpenRecordFile(); bool PrintMetaInfo(); bool ProcessRecord(std::unique_ptr<Record> record); + void UpdateThreadName(uint32_t pid, uint32_t tid); bool ProcessSampleRecord(const SampleRecord& r); bool PrintSampleRecordInProtobuf(const SampleRecord& record, const std::vector<CallEntry>& entries); @@ -142,6 +144,8 @@ class ReportSampleCommand : public Command { bool remove_unknown_kernel_symbols_; bool kernel_symbols_available_; bool show_art_frames_; + // map from <pid, tid> to thread name + std::map<uint64_t, const char*> thread_names_; }; bool ReportSampleCommand::Run(const std::vector<std::string>& args) { @@ -525,6 +529,8 @@ bool ReportSampleCommand::ProcessSampleRecord(const SampleRecord& r) { entries.push_back(entry); } if (use_protobuf_) { + uint64_t key = (static_cast<uint64_t>(r.tid_data.pid) << 32) | r.tid_data.tid; + thread_names_[key] = thread->comm; return PrintSampleRecordInProtobuf(r, entries); } return PrintSampleRecord(r, entries); @@ -646,17 +652,14 @@ bool ReportSampleCommand::PrintFileInfoInProtobuf() { } bool ReportSampleCommand::PrintThreadInfoInProtobuf() { - std::vector<const ThreadEntry*> threads = thread_tree_.GetAllThreads(); - auto compare_thread_id = [](const ThreadEntry* t1, const ThreadEntry* t2) { - return t1->tid < t2->tid; - }; - std::sort(threads.begin(), threads.end(), compare_thread_id); - for (auto& thread : threads) { + for (const auto& p : thread_names_) { + uint32_t pid = p.first >> 32; + uint32_t tid = p.first & std::numeric_limits<uint32_t>::max(); proto::Record proto_record; proto::Thread* proto_thread = proto_record.mutable_thread(); - proto_thread->set_thread_id(thread->tid); - proto_thread->set_process_id(thread->pid); - proto_thread->set_thread_name(thread->comm); + proto_thread->set_thread_id(tid); + proto_thread->set_process_id(pid); + proto_thread->set_thread_name(p.second); if (!WriteRecordInProtobuf(proto_record)) { return false; } diff --git a/simpleperf/cmd_report_sample_test.cpp b/simpleperf/cmd_report_sample_test.cpp index 34ba3f72..9543634d 100644 --- a/simpleperf/cmd_report_sample_test.cpp +++ b/simpleperf/cmd_report_sample_test.cpp @@ -80,6 +80,7 @@ TEST(cmd_report_sample, has_thread_record) { std::string data; GetProtobufReport(PERF_DATA_WITH_SYMBOLS, &data); ASSERT_NE(data.find("thread:"), std::string::npos); + ASSERT_NE(data.find("thread_name: t2"), std::string::npos); } TEST(cmd_report_sample, trace_offcpu) { diff --git a/simpleperf/sample_tree.h b/simpleperf/sample_tree.h index a2ce19fa..8578ffd5 100644 --- a/simpleperf/sample_tree.h +++ b/simpleperf/sample_tree.h @@ -145,7 +145,7 @@ class SampleTreeBuilder { } } EntryT* callchain_sample = - CreateCallChainSample(sample, ip, in_kernel, callchain, acc_info); + CreateCallChainSample(thread, sample, ip, in_kernel, callchain, acc_info); if (callchain_sample == nullptr) { break; } @@ -188,8 +188,8 @@ class SampleTreeBuilder { AccumulateInfoT* acc_info) = 0; virtual EntryT* CreateBranchSample(const SampleRecord& r, const BranchStackItemType& item) = 0; - virtual EntryT* CreateCallChainSample(const EntryT* sample, uint64_t ip, - bool in_kernel, + virtual EntryT* CreateCallChainSample(const ThreadEntry* thread, const EntryT* sample, + uint64_t ip, bool in_kernel, const std::vector<EntryT*>& callchain, const AccumulateInfoT& acc_info) = 0; virtual const ThreadEntry* GetThreadOfSample(EntryT*) = 0; diff --git a/simpleperf/sample_tree_test.cpp b/simpleperf/sample_tree_test.cpp index 7aa778c0..4123b0e6 100644 --- a/simpleperf/sample_tree_test.cpp +++ b/simpleperf/sample_tree_test.cpp @@ -76,7 +76,7 @@ class TestSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, int> { const BranchStackItemType&) override { return nullptr; }; - SampleEntry* CreateCallChainSample(const SampleEntry*, uint64_t, bool, + SampleEntry* CreateCallChainSample(const ThreadEntry*, const SampleEntry*, uint64_t, bool, const std::vector<SampleEntry*>&, const int&) override { return nullptr; diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py index 7c39e528..067c493a 100755 --- a/simpleperf/scripts/test.py +++ b/simpleperf/scripts/test.py @@ -647,7 +647,6 @@ class TestExampleWithNativeJniCall(TestExampleBase): self.run_cmd(["report.py", "-g", "--comms", "BusyThread", "-o", "report.txt"]) self.check_strings_in_file("report.txt", [ "com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run", - "com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction", "Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"]) remove("annotated_files") self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "BusyThread"]) diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp index 3b90d518..fc92ac07 100644 --- a/simpleperf/thread_tree.cpp +++ b/simpleperf/thread_tree.cpp @@ -46,6 +46,7 @@ void ThreadTree::ForkThread(int pid, int tid, int ppid, int ptid) { if (child->maps->maps.empty()) { *child->maps = *parent->maps; } else { + CHECK_NE(child->maps, parent->maps); for (auto& pair : parent->maps->maps) { InsertMap(*child->maps, *pair.second); } @@ -55,32 +56,30 @@ void ThreadTree::ForkThread(int pid, int tid, int ppid, int ptid) { ThreadEntry* ThreadTree::FindThreadOrNew(int pid, int tid) { auto it = thread_tree_.find(tid); - if (it == thread_tree_.end()) { - return CreateThread(pid, tid); - } else { - if (pid != it->second.get()->pid) { - // TODO: b/22185053. - LOG(DEBUG) << "unexpected (pid, tid) pair: expected (" - << it->second.get()->pid << ", " << tid << "), actual (" << pid - << ", " << tid << ")"; - } + if (it != thread_tree_.end() && pid == it->second.get()->pid) { + return it->second.get(); } - return it->second.get(); + if (it != thread_tree_.end()) { + ExitThread(it->second.get()->pid, tid); + } + return CreateThread(pid, tid); } ThreadEntry* ThreadTree::CreateThread(int pid, int tid) { - MapSet* maps = nullptr; + const char* comm; + std::shared_ptr<MapSet> maps; if (pid == tid) { - maps = new MapSet; - map_set_storage_.push_back(std::unique_ptr<MapSet>(maps)); + comm = "unknown"; + maps.reset(new MapSet); } else { // Share maps among threads in the same thread group. ThreadEntry* process = FindThreadOrNew(pid, pid); + comm = process->comm; maps = process->maps; } ThreadEntry* thread = new ThreadEntry{ pid, tid, - "unknown", + comm, maps, }; auto pair = thread_tree_.insert(std::make_pair(tid, std::unique_ptr<ThreadEntry>(thread))); @@ -88,6 +87,13 @@ ThreadEntry* ThreadTree::CreateThread(int pid, int tid) { return thread; } +void ThreadTree::ExitThread(int pid, int tid) { + auto it = thread_tree_.find(tid); + if (it != thread_tree_.end() && pid == it->second.get()->pid) { + thread_tree_.erase(it); + } +} + void ThreadTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, const std::string& filename) { // kernel map len can be 0 when record command is not run in supervisor mode. @@ -260,7 +266,6 @@ const Symbol* ThreadTree::FindKernelSymbol(uint64_t ip) { void ThreadTree::ClearThreadAndMap() { thread_tree_.clear(); thread_comm_storage_.clear(); - map_set_storage_.clear(); kernel_maps_.maps.clear(); map_storage_.clear(); } @@ -313,6 +318,9 @@ void ThreadTree::Update(const Record& record) { } else if (record.type() == PERF_RECORD_FORK) { const ForkRecord& r = *static_cast<const ForkRecord*>(&record); ForkThread(r.data->pid, r.data->tid, r.data->ppid, r.data->ptid); + } else if (record.type() == PERF_RECORD_EXIT) { + const ExitRecord& r = *static_cast<const ExitRecord*>(&record); + ExitThread(r.data->pid, r.data->tid); } else if (record.type() == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) { const auto& r = *static_cast<const KernelSymbolRecord*>(&record); Dso::SetKallsyms(std::move(r.kallsyms)); @@ -332,12 +340,4 @@ std::vector<Dso*> ThreadTree::GetAllDsos() const { return result; } -std::vector<const ThreadEntry*> ThreadTree::GetAllThreads() const { - std::vector<const ThreadEntry*> threads; - for (auto& pair : thread_tree_) { - threads.push_back(pair.second.get()); - } - return threads; -} - } // namespace simpleperf diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h index 3094d700..3f438365 100644 --- a/simpleperf/thread_tree.h +++ b/simpleperf/thread_tree.h @@ -69,7 +69,7 @@ struct ThreadEntry { int pid; int tid; const char* comm; // It always refers to the latest comm. - MapSet* maps; + std::shared_ptr<MapSet> maps; // maps is shared by threads in the same process. }; // ThreadTree contains thread information (in ThreadEntry) and mmap information @@ -93,6 +93,7 @@ class ThreadTree { void SetThreadName(int pid, int tid, const std::string& comm); void ForkThread(int pid, int tid, int ppid, int ptid); ThreadEntry* FindThreadOrNew(int pid, int tid); + void ExitThread(int pid, int tid); void AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, const std::string& filename); void AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len, @@ -125,7 +126,6 @@ class ThreadTree { void Update(const Record& record); std::vector<Dso*> GetAllDsos() const; - std::vector<const ThreadEntry*> GetAllThreads() const; private: ThreadEntry* CreateThread(int pid, int tid); @@ -138,7 +138,6 @@ class ThreadTree { std::unordered_map<int, std::unique_ptr<ThreadEntry>> thread_tree_; std::vector<std::unique_ptr<std::string>> thread_comm_storage_; - std::vector<std::unique_ptr<MapSet>> map_set_storage_; MapSet kernel_maps_; std::vector<std::unique_ptr<MapEntry>> map_storage_; MapEntry unknown_map_; diff --git a/simpleperf/thread_tree_test.cpp b/simpleperf/thread_tree_test.cpp index be3d8bfa..d00ebcb9 100644 --- a/simpleperf/thread_tree_test.cpp +++ b/simpleperf/thread_tree_test.cpp @@ -105,3 +105,18 @@ TEST_F(ThreadTreeTest, jit_maps_before_fork) { ASSERT_TRUE(map != nullptr); ASSERT_EQ(map->flags, map_flags::PROT_JIT_SYMFILE_MAP); } + +TEST_F(ThreadTreeTest, reused_tid) { + // Process 1 has thread 1 and 2. + thread_tree_.ForkThread(1, 2, 1, 1); + // Thread 2 exits. + thread_tree_.ExitThread(1, 2); + // Thread 1 forks process 2. + thread_tree_.ForkThread(2, 2, 1, 1); +} + +TEST_F(ThreadTreeTest, reused_tid_without_thread_exit) { + // Similar to the above test, but the thread exit record is missing. + thread_tree_.ForkThread(1, 2, 1, 1); + thread_tree_.ForkThread(2, 2, 1, 1); +} |