Merge "simpleperf: add thread name in report_sample.proto." am: b33edada95
am: f13c66de88

Change-Id: Ied50dc7eb9a3d663f624fb4aec3ca42d1e1ed6d5
diff --git a/simpleperf/cmd_report_sample.cpp b/simpleperf/cmd_report_sample.cpp
index f43f682..73af7d9 100644
--- a/simpleperf/cmd_report_sample.cpp
+++ b/simpleperf/cmd_report_sample.cpp
@@ -92,8 +92,10 @@
                     uint64_t* pvaddr_in_file, uint32_t* pfile_id, int32_t* psymbol_id);
   bool GetCallEntry(const ThreadEntry* thread, bool in_kernel, uint64_t ip, bool omit_unknown_dso,
                     uint64_t* pvaddr_in_file, Dso** pdso, const Symbol** psymbol);
+  bool WriteRecordInProtobuf(proto::Record& proto_record);
   bool PrintLostSituationInProtobuf();
   bool PrintFileInfoInProtobuf();
+  bool PrintThreadInfoInProtobuf();
   bool PrintSampleRecord(const SampleRecord& record);
   void PrintLostSituation();
 
@@ -178,6 +180,9 @@
     if (!PrintFileInfoInProtobuf()) {
       return false;
     }
+    if (!PrintThreadInfoInProtobuf()) {
+      return false;
+    }
     coded_os_->WriteLittleEndian32(0);
     if (coded_os_->HadError()) {
       LOG(ERROR) << "print protobuf report failed";
@@ -313,6 +318,12 @@
         return false;
       }
       files.push_back(file.symbol_size());
+    } else if (proto_record.has_thread()) {
+      auto& thread = proto_record.thread();
+      FprintIndented(report_fp_, 0, "thread:\n");
+      FprintIndented(report_fp_, 1, "thread_id: %u\n", thread.thread_id());
+      FprintIndented(report_fp_, 1, "process_id: %u\n", thread.process_id());
+      FprintIndented(report_fp_, 1, "thread_name: %s\n", thread.thread_name().c_str());
     } else {
       LOG(ERROR) << "unexpected record type ";
       return false;
@@ -404,9 +415,13 @@
       }
     }
   }
+  return WriteRecordInProtobuf(proto_record);
+}
+
+bool ReportSampleCommand::WriteRecordInProtobuf(proto::Record& proto_record) {
   coded_os_->WriteLittleEndian32(proto_record.ByteSize());
   if (!proto_record.SerializeToCodedStream(coded_os_)) {
-    LOG(ERROR) << "failed to write sample to protobuf";
+    LOG(ERROR) << "failed to write record to protobuf";
     return false;
   }
   return true;
@@ -458,12 +473,7 @@
   proto::LostSituation* lost = proto_record.mutable_lost();
   lost->set_sample_count(sample_count_);
   lost->set_lost_count(lost_count_);
-  coded_os_->WriteLittleEndian32(proto_record.ByteSize());
-  if (!proto_record.SerializeToCodedStream(coded_os_)) {
-    LOG(ERROR) << "failed to write lost situation to protobuf";
-    return false;
-  }
-  return true;
+  return WriteRecordInProtobuf(proto_record);
 }
 
 static bool CompareDsoByDumpId(Dso* d1, Dso* d2) {
@@ -500,9 +510,26 @@
       std::string* symbol = file->add_symbol();
       *symbol = sym->DemangledName();
     }
-    coded_os_->WriteLittleEndian32(proto_record.ByteSize());
-    if (!proto_record.SerializeToCodedStream(coded_os_)) {
-      LOG(ERROR) << "failed to write file info to protobuf";
+    if (!WriteRecordInProtobuf(proto_record)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+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) {
+    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);
+    if (!WriteRecordInProtobuf(proto_record)) {
       return false;
     }
   }
diff --git a/simpleperf/cmd_report_sample_test.cpp b/simpleperf/cmd_report_sample_test.cpp
index 3e92396..2a712be 100644
--- a/simpleperf/cmd_report_sample_test.cpp
+++ b/simpleperf/cmd_report_sample_test.cpp
@@ -79,3 +79,15 @@
   ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, &data));
   ASSERT_NE(data.find("event_count:"), std::string::npos);
 }
+
+TEST(cmd_report_sample, has_thread_record) {
+  TemporaryFile tmpfile;
+  TemporaryFile tmpfile2;
+  ASSERT_TRUE(ReportSampleCmd()->Run({"-i", GetTestData(PERF_DATA_WITH_SYMBOLS),
+                                      "-o", tmpfile.path, "--protobuf"}));
+  ASSERT_TRUE(ReportSampleCmd()->Run(
+      {"--dump-protobuf-report", tmpfile.path, "-o", tmpfile2.path}));
+  std::string data;
+  ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, &data));
+  ASSERT_NE(data.find("thread:"), std::string::npos);
+}
diff --git a/simpleperf/report_sample.proto b/simpleperf/report_sample.proto
index 5e1d860..a40f803 100644
--- a/simpleperf/report_sample.proto
+++ b/simpleperf/report_sample.proto
@@ -59,10 +59,17 @@
   repeated string symbol = 3;
 }
 
+message Thread {
+  optional uint32 thread_id = 1;
+  optional uint32 process_id = 2;
+  optional string thread_name = 3;
+}
+
 message Record {
   oneof record_data {
     Sample sample = 1;
     LostSituation lost = 2;
     File file = 3;
+    Thread thread = 4;
   }
 }
\ No newline at end of file
diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp
index a6f86a8..92a8a8f 100644
--- a/simpleperf/thread_tree.cpp
+++ b/simpleperf/thread_tree.cpp
@@ -327,4 +327,12 @@
   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 8df6b7b..621bc46 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -119,6 +119,7 @@
   void Update(const Record& record);
 
   std::vector<Dso*> GetAllDsos() const;
+  std::vector<const ThreadEntry*> GetAllThreads() const;
 
  private:
   ThreadEntry* CreateThread(int pid, int tid);