Merge "f2fs_utils: update f2fs utils to match 1.8.0"
diff --git a/simpleperf/README.md b/simpleperf/README.md
index 74b7fb7..b6dc9d7 100644
--- a/simpleperf/README.md
+++ b/simpleperf/README.md
@@ -523,6 +523,8 @@
     # Open SimpleperfExamplesPureJava project with Android studio,
     # and build this project sucessfully, otherwise the `./gradlew` command below will fail.
     $cd SimpleperfExamplePureJava
+
+    # On windows, use "gradlew" instead.
     $./gradlew clean assemble
     $adb install -r app/build/outputs/apk/app-profiling.apk
 
@@ -618,6 +620,7 @@
     $adb shell run-as com.example.simpleperf.simpleperfexamplepurejava cat perf.data >perf.data
 
     # Report samples using corresponding simpleperf executable on host.
+    # On windows, use "bin\windows\x86_64\simpleperf" instead.
     $bin/linux/x86_64/simpleperf report
     ...
     Overhead  Command   Pid   Tid   Shared Object                                                                     Symbol
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/demo/README.md b/simpleperf/demo/README.md
index 2376e94..3737416 100644
--- a/simpleperf/demo/README.md
+++ b/simpleperf/demo/README.md
@@ -35,6 +35,8 @@
     # Open SimpleperfExamplesPureJava project with Android Studio,
     # and build this project sucessfully, otherwise the `./gradlew` command below will fail.
     $cd SimpleperfExamplePureJava
+
+    # On windows, use "gradlew" instead.
     $./gradlew clean assemble
     $adb install -r app/build/outputs/apk/app-profiling.apk
 
@@ -49,6 +51,7 @@
 3. Show profiling data:
 
     a. show call graph in txt mode
+        # On windows, use "bin\windows\x86\simpleperf" instead.
         $bin/linux/x86_64/simpleperf report -g --brief-callgraph | more
           If on other hosts, use corresponding simpleperf binary.
     b. show call graph in gui mode
@@ -72,6 +75,8 @@
     # Open SimpleperfExamplesPureJava project with Android Studio,
     # and build this project sucessfully, otherwise the `./gradlew` command below will fail.
     $cd SimpleperfExampleWithNative
+
+    # On windows, use "gradlew" instead.
     $./gradlew clean assemble
     $adb install -r app/build/outputs/apk/app-profiling.apk
 
@@ -86,6 +91,7 @@
 3. Show profiling data:
 
     a. show call graph in txt mode
+        # On windows, use "bin\windows\x86\simpleperf" instead.
         $bin/linux/x86_64/simpleperf report -g --brief-callgraph | more
           If on other hosts, use corresponding simpleperf binary.
     b. show call graph in gui mode
diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp
index 18eff2d..c6edb24 100644
--- a/simpleperf/event_fd.cpp
+++ b/simpleperf/event_fd.cpp
@@ -103,7 +103,7 @@
 uint64_t EventFd::Id() const {
   if (id_ == 0) {
     PerfCounter counter;
-    if (ReadCounter(&counter)) {
+    if (InnerReadCounter(&counter)) {
       id_ = counter.id;
     }
   }
@@ -119,23 +119,30 @@
   return true;
 }
 
-bool EventFd::ReadCounter(PerfCounter* counter) const {
+bool EventFd::InnerReadCounter(PerfCounter* counter) const {
   CHECK(counter != nullptr);
-  uint64_t pre_counter = counter->value;
   if (!android::base::ReadFully(perf_event_fd_, counter, sizeof(*counter))) {
     PLOG(ERROR) << "ReadCounter from " << Name() << " failed";
     return false;
   }
+  return true;
+}
+
+bool EventFd::ReadCounter(PerfCounter* counter) {
+  if (!InnerReadCounter(counter)) {
+    return false;
+  }
   // Trace is always available to systrace if enabled
   if (tid_ > 0) {
     ATRACE_INT64(android::base::StringPrintf(
                    "%s_tid%d_cpu%d", event_name_.c_str(), tid_,
-                   cpu_).c_str(), counter->value - pre_counter);
+                   cpu_).c_str(), counter->value - last_counter_value_);
   } else {
     ATRACE_INT64(android::base::StringPrintf(
                    "%s_cpu%d", event_name_.c_str(),
-                   cpu_).c_str(), counter->value - pre_counter);
+                   cpu_).c_str(), counter->value - last_counter_value_);
   }
+  last_counter_value_ = counter->value;
   return true;
 }
 
diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h
index f1ddb55..ab47cc5 100644
--- a/simpleperf/event_fd.h
+++ b/simpleperf/event_fd.h
@@ -60,7 +60,7 @@
   // this file.
   bool EnableEvent();
 
-  bool ReadCounter(PerfCounter* counter) const;
+  bool ReadCounter(PerfCounter* counter);
 
   // Create mapped buffer used to receive records sent by the kernel.
   // mmap_pages should be power of 2.
@@ -96,8 +96,10 @@
         mmap_metadata_page_(nullptr),
         mmap_data_buffer_(nullptr),
         mmap_data_buffer_size_(0),
-        ioevent_ref_(nullptr) {}
+        ioevent_ref_(nullptr),
+        last_counter_value_(0) {}
 
+  bool InnerReadCounter(PerfCounter* counter) const;
   // Discard how much data we have read, so the kernel can reuse this part of
   // mapped area to store new data.
   void DiscardMmapData(size_t discard_size);
@@ -123,6 +125,9 @@
 
   IOEventRef ioevent_ref_;
 
+  // Used by atrace to generate value difference between two ReadCounter() calls.
+  uint64_t last_counter_value_;
+
   DISALLOW_COPY_AND_ASSIGN(EventFd);
 };
 
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index b966273..86f57b9 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -450,7 +450,7 @@
   return true;
 }
 
-static bool ReadCounter(const EventFd* event_fd, CounterInfo* counter) {
+static bool ReadCounter(EventFd* event_fd, CounterInfo* counter) {
   if (!event_fd->ReadCounter(&counter->counter)) {
     return false;
   }
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/scripts/annotate.py b/simpleperf/scripts/annotate.py
index 1f3a2ef..d88a916 100644
--- a/simpleperf/scripts/annotate.py
+++ b/simpleperf/scripts/annotate.py
@@ -111,7 +111,11 @@
                 function = stdoutdata[out_pos]
                 out_pos += 1
                 assert out_pos < len(stdoutdata)
-                file, line = stdoutdata[out_pos].split(':')
+                # Handle lines like "C:\Users\...\file:32".
+                items = stdoutdata[out_pos].rsplit(':', 1)
+                if len(items) != 2:
+                    continue
+                (file, line) = items
                 line = line.split()[0]  # Remove comments after line number
                 out_pos += 1
                 if file.find('?') != -1:
@@ -123,8 +127,8 @@
                 else:
                     line = int(line)
                 source_lines.append(SourceLine(file, function, line))
-                dso[addrs[addr_pos]] = source_lines
-                addr_pos += 1
+            dso[addrs[addr_pos]] = source_lines
+            addr_pos += 1
         assert addr_pos == len(addrs)
 
 
@@ -557,6 +561,9 @@
                 path = key
                 from_path = path
                 to_path = os.path.join(dest_dir, path[1:])
+            elif is_windows() and key.find(':\\') != -1 and os.path.isfile(key):
+                from_path = key
+                to_path = os.path.join(dest_dir, key.replace(':\\', '\\'))
             else:
                 path = key[1:] if key.startswith('/') else key
                 # Change path on device to path on host
@@ -626,3 +633,4 @@
     config = load_config(args.config)
     annotator = SourceFileAnnotator(config)
     annotator.annotate()
+    log_info('annotate finish successfully, please check result in annotated_files/.')
\ No newline at end of file
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
index 27ce08a..de90466 100644
--- a/simpleperf/scripts/simpleperf_report_lib.py
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -167,14 +167,9 @@
         self.convert_to_str = (sys.version_info >= (3, 0))
 
     def _load_dependent_lib(self):
-        # As the windows dll is built with mingw we need to also find "libwinpthread-1.dll".
-        # Load it before libsimpleperf_report.dll if it does exist in the same folder as this script.
+        # As the windows dll is built with mingw we need to load "libwinpthread-1.dll".
         if is_windows():
-            libwinpthread_path = os.path.join(get_script_dir(), "libwinpthread-1.dll")
-            if os.path.exists(libwinpthread_path):
-                self._libwinpthread = ct.CDLL(libwinpthread_path)
-            else:
-                log_fatal('%s is missing' % libwinpthread_path)
+            self._libwinpthread = ct.CDLL(get_host_binary_path('libwinpthread-1.dll'))
 
     def Close(self):
         if self._instance is None:
diff --git a/simpleperf/scripts/update.py b/simpleperf/scripts/update.py
index 7065ef0..fbdc404 100644
--- a/simpleperf/scripts/update.py
+++ b/simpleperf/scripts/update.py
@@ -46,7 +46,8 @@
     InstallEntry('sdk_arm64-sdk', 'simpleperf_host32', 'linux/x86/simpleperf', True),
     InstallEntry('sdk_mac', 'simpleperf_host', 'darwin/x86_64/simpleperf'),
     InstallEntry('sdk_mac', 'simpleperf_host32', 'darwin/x86/simpleperf'),
-    InstallEntry('sdk', 'simpleperf.exe', 'windows/x86_64/simpleperf.exe', True),
+    # simpleperf.exe on x86_64 windows doesn't work, use simpleperf32.exe instead.
+    InstallEntry('sdk', 'simpleperf32.exe', 'windows/x86_64/simpleperf.exe', True),
     InstallEntry('sdk', 'simpleperf32.exe', 'windows/x86/simpleperf.exe', True),
 
     # libsimpleperf_report.so on host
@@ -56,6 +57,12 @@
     InstallEntry('sdk_mac', 'libsimpleperf_report32.so', 'darwin/x86/libsimpleperf_report.dylib'),
     InstallEntry('sdk', 'libsimpleperf_report.dll', 'windows/x86_64/libsimpleperf_report.dll', True),
     InstallEntry('sdk', 'libsimpleperf_report32.dll', 'windows/x86/libsimpleperf_report.dll', True),
+
+    # libwinpthread-1.dll on windows host
+    InstallEntry('local', '../../../../prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/bin/libwinpthread-1.dll',
+                 'windows/x86_64/libwinpthread-1.dll', False),
+    InstallEntry('local', '../../../../prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib32/libwinpthread-1.dll',
+                 'windows/x86/libwinpthread-1.dll', False),
 ]
 
 
@@ -73,6 +80,8 @@
 
 def fetch_artifact(branch, build, target, pattern):
     """Fetches and artifact from the build server."""
+    if target == 'local':
+        return
     logger().info('Fetching %s from %s %s (artifacts matching %s)', build,
                   target, branch, pattern)
     fetch_artifact_path = '/google/data/ro/projects/android/fetch_artifact'
diff --git a/simpleperf/scripts/utils.py b/simpleperf/scripts/utils.py
index ab76c3f..d5515ba 100644
--- a/simpleperf/scripts/utils.py
+++ b/simpleperf/scripts/utils.py
@@ -65,6 +65,8 @@
     if is_windows():
         if binary_name.endswith('.so'):
             binary_name = binary_name[0:-3] + '.dll'
+        elif binary_name.find('.') == -1:
+            binary_name += '.exe'
         dir = os.path.join(dir, 'windows')
     elif sys.platform == 'darwin': # OSX
         if binary_name.endswith('.so'):
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_