Merge "simpleperf: add META_INFO feature section in perf.data."
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 191d641..2ce5295 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -49,7 +49,7 @@
   void DumpFileHeader();
   void DumpAttrSection();
   void DumpDataSection();
-  void DumpFeatureSection();
+  bool DumpFeatureSection();
 
   std::string record_filename_;
   std::unique_ptr<RecordFileReader> record_file_reader_;
@@ -75,7 +75,9 @@
   DumpFileHeader();
   DumpAttrSection();
   DumpDataSection();
-  DumpFeatureSection();
+  if (!DumpFeatureSection()) {
+    return false;
+  }
 
   return true;
 }
@@ -180,7 +182,7 @@
   }, false);
 }
 
-void DumpRecordCommand::DumpFeatureSection() {
+bool DumpRecordCommand::DumpFeatureSection() {
   std::map<int, SectionDesc> section_map = record_file_reader_->FeatureSectionDescriptors();
   for (const auto& pair : section_map) {
     int feature = pair.first;
@@ -220,8 +222,18 @@
                         symbol.addr, symbol.addr + symbol.len);
         }
       }
+    } else if (feature == FEAT_META_INFO) {
+      std::unordered_map<std::string, std::string> info_map;
+      if (!record_file_reader_->ReadMetaInfoFeature(&info_map)) {
+        return false;
+      }
+      PrintIndented(1, "meta_info:\n");
+      for (auto& pair : info_map) {
+        PrintIndented(2, "%s = %s\n", pair.first.c_str(), pair.second.c_str());
+      }
     }
   }
+  return true;
 }
 
 void RegisterDumpRecordCommand() {
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 3376af0..78e0a95 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -941,7 +941,7 @@
     return false;
   }
 
-  size_t feature_count = 4;
+  size_t feature_count = 5;
   if (branch_sampling_) {
     feature_count++;
   }
@@ -984,6 +984,13 @@
       !record_file_writer_->WriteBranchStackFeature()) {
     return false;
   }
+
+  std::unordered_map<std::string, std::string> info_map;
+  info_map["simpleperf_version"] = GetSimpleperfVersion();
+  if (!record_file_writer_->WriteMetaInfoFeature(info_map)) {
+    return false;
+  }
+
   if (!record_file_writer_->EndWriteFeatures()) {
     return false;
   }
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 85bbe34..ec0605a 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -428,3 +428,13 @@
   close(read_fd);
   ASSERT_EQ("STARTED", s);
 }
+
+TEST(record_cmd, record_meta_info_feature) {
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
+  std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+  ASSERT_TRUE(reader != nullptr);
+  std::unordered_map<std::string, std::string> info_map;
+  ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map));
+  ASSERT_NE(info_map.find("simpleperf_version"), info_map.end());
+}
diff --git a/simpleperf/main.cpp b/simpleperf/main.cpp
index 07b0e4b..ae3e629 100644
--- a/simpleperf/main.cpp
+++ b/simpleperf/main.cpp
@@ -24,8 +24,6 @@
 #include "command.h"
 #include "utils.h"
 
-constexpr int SIMPLEPERF_VERSION = 1;
-
 int main(int argc, char** argv) {
   android::base::InitLogging(argv, android::base::StderrLogger);
   std::vector<std::string> args;
@@ -46,8 +44,7 @@
         return 1;
       }
     } else if (strcmp(argv[i], "--version") == 0) {
-      LOG(INFO) << "Simpleperf version " << SIMPLEPERF_VERSION << ", revision "
-                << SIMPLEPERF_REVISION;
+      LOG(INFO) << "Simpleperf version " << GetSimpleperfVersion();
       return 0;
     } else {
       args.push_back(argv[i]);
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index 51d4f43..32c91fa 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -56,6 +56,7 @@
                         uint32_t file_type,
                         uint64_t min_vaddr,
                         const std::vector<const Symbol*>& symbols);
+  bool WriteMetaInfoFeature(const std::unordered_map<std::string, std::string>& info_map);
   bool EndWriteFeatures();
 
   // Normally, Close() should be called after writing. But if something
@@ -148,6 +149,7 @@
   bool ReadFileFeature(size_t& read_pos, std::string* file_path,
                        uint32_t* file_type, uint64_t* min_vaddr,
                        std::vector<Symbol>* symbols);
+  bool ReadMetaInfoFeature(std::unordered_map<std::string, std::string>* info_map);
 
   void LoadBuildIdAndFileFeatures(ThreadTree& thread_tree);
 
diff --git a/simpleperf/record_file_format.h b/simpleperf/record_file_format.h
index efa000c..f9ed6f3 100644
--- a/simpleperf/record_file_format.h
+++ b/simpleperf/record_file_format.h
@@ -19,34 +19,47 @@
 
 #include "perf_event.h"
 
-// The file structure of perf.data:
-//    file_header
-//    id_section
-//    attr section
-//    data section
-//    feature section
-//
-//  The feature section has the following structure:
-//    a section descriptor array, each element contains the section information of one add_feature.
-//    data section of feature 1
-//    data section of feature 2
-//    ....
+/*
+The file structure of perf.data:
+    file_header
+    id_section
+    attr section
+    data section
+    feature section
 
-// file feature section:
-//  file_struct files[];
-//
-//  struct file_struct {
-//    uint32_t size;  // size of rest fields in file_struct
-//    char file_path[];
-//    uint32_t file_type;
-//    uint64_t min_vaddr;
-//    uint32_t symbol_count;
-//    struct {
-//      uint64_t start_vaddr;
-//      uint32_t len;
-//      char symbol_name[];
-//    } symbol_table;
-//  };
+The feature section has the following structure:
+    a section descriptor array, each element contains the section information of one add_feature.
+    data section of feature 1
+    data section of feature 2
+    ....
+
+file feature section:
+  file_struct files[];
+
+  struct file_struct {
+    uint32_t size;  // size of rest fields in file_struct
+    char file_path[];
+    uint32_t file_type;
+    uint64_t min_vaddr;
+    uint32_t symbol_count;
+    struct {
+      uint64_t start_vaddr;
+      uint32_t len;
+      char symbol_name[];
+    } symbol_table;
+  };
+
+meta_info feature section:
+  meta_info infos[];
+
+  struct meta_info {
+    char key[];
+    char value[];
+  };
+  keys in meta_info feature section include:
+    simpleperf_version,
+
+*/
 
 namespace PerfFileFormat {
 
@@ -74,6 +87,7 @@
 
   FEAT_SIMPLEPERF_START = 128,
   FEAT_FILE = FEAT_SIMPLEPERF_START,
+  FEAT_META_INFO,
   FEAT_MAX_NUM = 256,
 };
 
diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp
index 0fdb6dd..67f35be 100644
--- a/simpleperf/record_file_reader.cpp
+++ b/simpleperf/record_file_reader.cpp
@@ -434,6 +434,23 @@
   return true;
 }
 
+bool RecordFileReader::ReadMetaInfoFeature(std::unordered_map<std::string, std::string>* info_map) {
+  std::vector<char> buf;
+  if (!ReadFeatureSection(FEAT_META_INFO, &buf)) {
+    return false;
+  }
+  const char* p = buf.data();
+  const char* end = buf.data() + buf.size();
+  while (p < end) {
+    const char* key = p;
+    const char* value = key + strlen(key) + 1;
+    CHECK(value < end);
+    (*info_map)[p] = value;
+    p = value + strlen(value) + 1;
+  }
+  return true;
+}
+
 void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) {
   std::vector<BuildIdRecord> records = ReadBuildIdFeature();
   std::vector<std::pair<std::string, BuildId>> build_ids;
diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp
index 4fe725b..530611e 100644
--- a/simpleperf/record_file_test.cpp
+++ b/simpleperf/record_file_test.cpp
@@ -157,3 +157,29 @@
     ASSERT_EQ(attrs[i].ids, attr_ids_[i].ids);
   }
 }
+
+TEST_F(RecordFileTest, write_meta_info_feature_section) {
+  // Write to a record file.
+  std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
+  ASSERT_TRUE(writer != nullptr);
+  AddEventType("cpu-cycles");
+  ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
+
+  // Write meta_info feature section.
+  ASSERT_TRUE(writer->BeginWriteFeatures(1));
+  std::unordered_map<std::string, std::string> info_map;
+  for (int i = 0; i < 100; ++i) {
+    std::string s = std::to_string(i);
+    info_map[s] = s + s;
+  }
+  ASSERT_TRUE(writer->WriteMetaInfoFeature(info_map));
+  ASSERT_TRUE(writer->EndWriteFeatures());
+  ASSERT_TRUE(writer->Close());
+
+  // Read from a record file.
+  std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
+  ASSERT_TRUE(reader != nullptr);
+  std::unordered_map<std::string, std::string> read_info_map;
+  ASSERT_TRUE(reader->ReadMetaInfoFeature(&read_info_map));
+  ASSERT_EQ(read_info_map, info_map);
+}
diff --git a/simpleperf/record_file_writer.cpp b/simpleperf/record_file_writer.cpp
index fe62ab5..083a746 100644
--- a/simpleperf/record_file_writer.cpp
+++ b/simpleperf/record_file_writer.cpp
@@ -328,6 +328,28 @@
   return WriteFeatureEnd(FEAT_FILE);
 }
 
+bool RecordFileWriter::WriteMetaInfoFeature(
+    const std::unordered_map<std::string, std::string>& info_map) {
+  uint32_t size = 0u;
+  for (auto& pair : info_map) {
+    size += pair.first.size() + 1;
+    size += pair.second.size() + 1;
+  }
+  std::vector<char> buf(size);
+  char* p = buf.data();
+  for (auto& pair : info_map) {
+    MoveToBinaryFormat(pair.first.c_str(), pair.first.size() + 1, p);
+    MoveToBinaryFormat(pair.second.c_str(), pair.second.size() + 1, p);
+  }
+  if (!WriteFeatureBegin(FEAT_META_INFO)) {
+    return false;
+  }
+  if (!Write(buf.data(), buf.size())) {
+    return false;
+  }
+  return WriteFeatureEnd(FEAT_META_INFO);
+}
+
 bool RecordFileWriter::WriteFeatureBegin(int feature) {
   auto it = features_.find(feature);
   if (it == features_.end()) {
diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp
index 62a8b63..1dbe078 100644
--- a/simpleperf/utils.cpp
+++ b/simpleperf/utils.cpp
@@ -31,6 +31,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 
 #include <7zCrc.h>
 #include <Xz.h>
@@ -337,3 +338,9 @@
   tv.tv_usec = static_cast<int>((time_in_sec - tv.tv_sec) * 1000000);
   return tv;
 }
+
+constexpr int SIMPLEPERF_VERSION = 1;
+
+std::string GetSimpleperfVersion() {
+  return android::base::StringPrintf("%d.%s", SIMPLEPERF_VERSION, SIMPLEPERF_REVISION);
+}
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
index fc21a99..775fd5c 100644
--- a/simpleperf/utils.h
+++ b/simpleperf/utils.h
@@ -169,4 +169,6 @@
 
 timeval SecondToTimeval(double time_in_sec);
 
+std::string GetSimpleperfVersion();
+
 #endif  // SIMPLE_PERF_UTILS_H_