summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--simpleperf/SampleComparator.h4
-rw-r--r--simpleperf/SampleDisplayer.h4
-rw-r--r--simpleperf/cmd_kmem.cpp2
-rw-r--r--simpleperf/cmd_report.cpp17
-rw-r--r--simpleperf/cmd_report_sample.cpp21
-rw-r--r--simpleperf/cmd_report_sample_test.cpp1
-rw-r--r--simpleperf/sample_tree.h6
-rw-r--r--simpleperf/sample_tree_test.cpp2
-rwxr-xr-xsimpleperf/scripts/test.py1
-rw-r--r--simpleperf/thread_tree.cpp46
-rw-r--r--simpleperf/thread_tree.h5
-rw-r--r--simpleperf/thread_tree_test.cpp15
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);
+}