OSDN Git Service

simpleperf: report symbols of native libraries in apk file.
authorYabin Cui <yabinc@google.com>
Mon, 15 Feb 2016 03:18:02 +0000 (19:18 -0800)
committerYabin Cui <yabinc@google.com>
Mon, 7 Mar 2016 20:05:22 +0000 (12:05 -0800)
Changes included:
1. provide interface in read_apk.h to read build id and symbols.
2. report symbols of native libraries in apk file.
3. refactor code in read_elf.cpp and read_apk.cpp.
4. add verbose log.
5. add -o report_file_name option for report command.
6. add corresponding unit tests.

Bug: 26962895
Change-Id: I0d5398996e0c29dba4a6f5226692b758ca096bbd
(cherry picked from commit b1a885b014540a2f7798b5a35ea0f0ec150d93ee)

27 files changed:
simpleperf/build_id.h
simpleperf/cmd_help.cpp
simpleperf/cmd_record.cpp
simpleperf/cmd_report.cpp
simpleperf/cmd_report_test.cpp
simpleperf/cmd_stat_test.cpp
simpleperf/dso.cpp
simpleperf/dso.h
simpleperf/event_selection_set.cpp
simpleperf/get_test_data.h
simpleperf/gtest_main.cpp
simpleperf/main.cpp
simpleperf/read_apk.cpp
simpleperf/read_apk.h
simpleperf/read_apk_test.cpp
simpleperf/read_elf.cpp
simpleperf/read_elf.h
simpleperf/read_elf_test.cpp
simpleperf/record.cpp
simpleperf/record.h
simpleperf/test_util.h
simpleperf/testdata/data/app/com.example.hellojni-1/base.apk [new file with mode: 0644]
simpleperf/testdata/fibonacci.jar [deleted file]
simpleperf/testdata/has_embedded_native_libs.apk [deleted file]
simpleperf/testdata/has_embedded_native_libs_apk_perf.data [new file with mode: 0644]
simpleperf/utils.cpp
simpleperf/utils.h

index 05c37d5..bbd13c4 100644 (file)
@@ -33,10 +33,29 @@ class BuildId {
     memset(data_, '\0', BUILD_ID_SIZE);
   }
 
-  BuildId(const void* data, size_t len = BUILD_ID_SIZE) : BuildId() {
+  // Copy build id from a byte array, like {0x76, 0x00, 0x32,...}.
+  BuildId(const void* data, size_t len) : BuildId() {
     memcpy(data_, data, std::min(len, BUILD_ID_SIZE));
   }
 
+  // Read build id from a hex string, like "7600329e31058e12b145d153ef27cd40e1a5f7b9".
+  explicit BuildId(const std::string& s) : BuildId() {
+    for (size_t i = 0; i < s.size() && i < BUILD_ID_SIZE * 2; i += 2) {
+      unsigned char ch = 0;
+      for (size_t j = i; j < i + 2; ++j) {
+        ch <<= 4;
+        if (s[j] >= '0' && s[j] <= '9') {
+          ch |= s[j] - '0';
+        } else if (s[j] >= 'a' && s[j] <= 'f') {
+          ch |= s[j] - 'a' + 10;
+        } else if (s[j] >= 'A' && s[j] <= 'F') {
+          ch |= s[j] - 'A' + 10;
+        }
+      }
+      data_[i / 2] = ch;
+    }
+  }
+
   const unsigned char* Data() const {
     return data_;
   }
@@ -53,6 +72,10 @@ class BuildId {
     return memcmp(data_, build_id.data_, BUILD_ID_SIZE) == 0;
   }
 
+  bool operator!=(const BuildId& build_id) const {
+    return !(*this == build_id);
+  }
+
   bool IsEmpty() const {
     static BuildId empty_build_id;
     return *this == empty_build_id;
index a29ef72..93a90ee 100644 (file)
@@ -60,7 +60,8 @@ void HelpCommand::PrintShortHelp() {
       "common options:\n"
       "    -h/--help     Print this help information.\n"
       "    --log <severity> Set the minimum severity of logging. Possible severities\n"
-      "                     include debug, warning, error, fatal. Default is warning.\n"
+      "                     include verbose, debug, warning, error, fatal. Default is\n"
+      "                     warning.\n"
       "subcommands:\n");
   for (auto& cmd_name : GetAllCommandNames()) {
     std::unique_ptr<Command> cmd = CreateCommandInstance(cmd_name);
index edeb64c..b913f11 100644 (file)
@@ -126,7 +126,8 @@ class RecordCommand : public Command {
         post_unwind_(false),
         child_inherit_(true),
         perf_mmap_pages_(16),
-        record_filename_("perf.data") {
+        record_filename_("perf.data"),
+        sample_record_count_(0) {
     signaled = false;
     scoped_signal_handler_.reset(
         new ScopedSignalHandler({SIGCHLD, SIGINT, SIGTERM}, signal_handler));
@@ -146,6 +147,7 @@ class RecordCommand : public Command {
   bool DumpThreadCommAndMmaps(bool all_threads, const std::vector<pid_t>& selected_threads);
   bool CollectRecordsFromKernel(const char* data, size_t size);
   bool ProcessRecord(Record* record);
+  void UpdateRecordForEmbeddedElfPath(Record* record);
   void UnwindRecord(Record* record);
   bool PostUnwind(const std::vector<std::string>& args);
   bool DumpAdditionalFeatures(const std::vector<std::string>& args);
@@ -153,7 +155,6 @@ class RecordCommand : public Command {
   void CollectHitFileInfo(Record* record);
   std::pair<std::string, uint64_t> TestForEmbeddedElf(Dso *dso, uint64_t pgoff);
 
-
   bool use_sample_freq_;    // Use sample_freq_ when true, otherwise using sample_period_.
   uint64_t sample_freq_;    // Sample 'sample_freq_' times per second.
   uint64_t sample_period_;  // Sample once when 'sample_period_' events occur.
@@ -180,10 +181,10 @@ class RecordCommand : public Command {
   std::unique_ptr<RecordFileWriter> record_file_writer_;
 
   std::set<std::string> hit_kernel_modules_;
-  std::set<std::pair<std::string, uint64_t> > hit_user_files_;
-  ApkInspector apk_inspector_;
+  std::set<std::string> hit_user_files_;
 
   std::unique_ptr<ScopedSignalHandler> scoped_signal_handler_;
+  uint64_t sample_record_count_;
 };
 
 bool RecordCommand::Run(const std::vector<std::string>& args) {
@@ -280,7 +281,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
       return false;
     }
   }
-
+  LOG(VERBOSE) << "Record " << sample_record_count_ << " samples.";
   return true;
 }
 
@@ -637,15 +638,52 @@ bool RecordCommand::CollectRecordsFromKernel(const char* data, size_t size) {
 }
 
 bool RecordCommand::ProcessRecord(Record* record) {
+  UpdateRecordForEmbeddedElfPath(record);
   BuildThreadTree(*record, &thread_tree_);
   CollectHitFileInfo(record);
   if (unwind_dwarf_callchain_ && !post_unwind_) {
     UnwindRecord(record);
   }
+  if (record->type() == PERF_RECORD_SAMPLE) {
+    sample_record_count_++;
+  }
   bool result = record_file_writer_->WriteData(record->BinaryFormat());
   return result;
 }
 
+template<class RecordType>
+void UpdateMmapRecordForEmbeddedElfPath(RecordType* record) {
+  RecordType& r = *record;
+  bool in_kernel = ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL);
+  if (!in_kernel && r.data.pgoff != 0) {
+    // For the case of a shared library "foobar.so" embedded
+    // inside an APK, we rewrite the original MMAP from
+    // ["path.apk" offset=X] to ["path.apk!/foobar.so" offset=W]
+    // so as to make the library name explicit. This update is
+    // done here (as part of the record operation) as opposed to
+    // on the host during the report, since we want to report
+    // the correct library name even if the the APK in question
+    // is not present on the host. The new offset W is
+    // calculated to be with respect to the start of foobar.so,
+    // not to the start of path.apk.
+    EmbeddedElf* ee = ApkInspector::FindElfInApkByOffset(r.filename, r.data.pgoff);
+    if (ee != nullptr) {
+      // Compute new offset relative to start of elf in APK.
+      r.data.pgoff -= ee->entry_offset();
+      r.filename = GetUrlInApk(r.filename, ee->entry_name());
+      r.AdjustSizeBasedOnData();
+    }
+  }
+}
+
+void RecordCommand::UpdateRecordForEmbeddedElfPath(Record* record) {
+  if (record->type() == PERF_RECORD_MMAP) {
+    UpdateMmapRecordForEmbeddedElfPath(static_cast<MmapRecord*>(record));
+  } else if (record->type() == PERF_RECORD_MMAP2) {
+    UpdateMmapRecordForEmbeddedElfPath(static_cast<Mmap2Record*>(record));
+  }
+}
+
 void RecordCommand::UnwindRecord(Record* record) {
   if (record->type() == PERF_RECORD_SAMPLE) {
     SampleRecord& r = *static_cast<SampleRecord*>(record);
@@ -746,7 +784,7 @@ bool RecordCommand::DumpBuildIdFeature() {
   std::vector<BuildIdRecord> build_id_records;
   BuildId build_id;
   // Add build_ids for kernel/modules.
-  for (auto& filename : hit_kernel_modules_) {
+  for (const auto& filename : hit_kernel_modules_) {
     if (filename == DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID) {
       if (!GetKernelBuildId(&build_id)) {
         LOG(DEBUG) << "can't read build_id for kernel";
@@ -768,29 +806,21 @@ bool RecordCommand::DumpBuildIdFeature() {
     }
   }
   // Add build_ids for user elf files.
-  for (auto& dso_origin : hit_user_files_) {
-    auto& filename = dso_origin.first;
-    auto& offset = dso_origin.second;
+  for (const auto& filename : hit_user_files_) {
     if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) {
       continue;
     }
-    EmbeddedElf *ee = apk_inspector_.FindElfInApkByMmapOffset(filename, offset);
-    if (ee) {
-      if (!GetBuildIdFromEmbeddedElfFile(filename,
-                                         ee->entry_offset(),
-                                         ee->entry_size(),
-                                         &build_id)) {
-        LOG(DEBUG) << "can't read build_id from archive file " << filename
-                   << "entry " << ee->entry_name();
+    auto tuple = SplitUrlInApk(filename);
+    if (std::get<0>(tuple)) {
+      if (!GetBuildIdFromApkFile(std::get<1>(tuple), std::get<2>(tuple), &build_id)) {
+        LOG(DEBUG) << "can't read build_id from file " << filename;
+        continue;
+      }
+    } else {
+      if (!GetBuildIdFromElfFile(filename, &build_id)) {
+        LOG(DEBUG) << "can't read build_id from file " << filename;
         continue;
       }
-      std::string ee_filename = filename + "!" + ee->entry_name();
-      build_id_records.push_back(CreateBuildIdRecord(false, UINT_MAX, build_id, ee_filename));
-      continue;
-    }
-    if (!GetBuildIdFromElfFile(filename, &build_id)) {
-      LOG(DEBUG) << "can't read build_id from file " << filename;
-      continue;
     }
     build_id_records.push_back(CreateBuildIdRecord(false, UINT_MAX, build_id, filename));
   }
@@ -809,51 +839,9 @@ void RecordCommand::CollectHitFileInfo(Record* record) {
     if (in_kernel) {
       hit_kernel_modules_.insert(map->dso->Path());
     } else {
-      auto apair = std::make_pair(map->dso->Path(), map->pgoff);
-      hit_user_files_.insert(apair);
+      hit_user_files_.insert(map->dso->Path());
     }
   }
-  if (record->type() == PERF_RECORD_MMAP) {
-    MmapRecord& r = *static_cast<MmapRecord*>(record);
-    bool in_kernel = ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL);
-    if (!in_kernel) {
-      const ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.data.pid, r.data.tid);
-      const MapEntry* map = thread_tree_.FindMap(thread, r.data.addr, in_kernel);
-      if (map->pgoff != 0u) {
-        std::pair<std::string, uint64_t> ee_info = TestForEmbeddedElf(map->dso, map->pgoff);
-        if (!ee_info.first.empty()) {
-          // For the case of a shared library "foobar.so" embedded
-          // inside an APK, we rewrite the original MMAP from
-          // ["path.apk" offset=X] to ["path.apk!foobar.so" offset=W]
-          // so as to make the library name explicit. This update is
-          // done here (as part of the record operation) as opposed to
-          // on the host during the report, since we want to report
-          // the correct library name even if the the APK in question
-          // is not present on the host. The new offset W is
-          // calculated to be with respect to the start of foobar.so,
-          // not to the start of path.apk.
-          const std::string& entry_name = ee_info.first;
-          uint64_t new_offset = ee_info.second;
-          std::string new_filename = r.filename + "!" + entry_name;
-          UpdateMmapRecord(&r, new_filename, new_offset);
-        }
-      }
-    }
-  }
-}
-
-std::pair<std::string, uint64_t> RecordCommand::TestForEmbeddedElf(Dso *dso, uint64_t pgoff)
-{
-  // Examine the DSO to determine whether it corresponds to an ELF
-  // file embedded in an APK.
-  std::string ee_name;
-  EmbeddedElf *ee = apk_inspector_.FindElfInApkByMmapOffset(dso->Path(), pgoff);
-  if (ee) {
-    // Compute new offset relative to start of elf in APK.
-    uint64_t elf_offset = pgoff - ee->entry_offset();
-    return std::make_pair(ee->entry_name(), elf_offset);
-  }
-  return std::make_pair(std::string(), 0u);
 }
 
 __attribute__((constructor)) static void RegisterRecordCommand() {
index b667de6..9fbf82e 100644 (file)
@@ -256,6 +256,7 @@ class ReportCommand : public Command {
             "    -i <file>     Specify path of record file, default is perf.data.\n"
             "    -n            Print the sample count for each item.\n"
             "    --no-demangle        Don't demangle symbol names.\n"
+            "    -o report_file_name  Set report file name, default is stdout.\n"
             "    --pid pid1,pid2,...\n"
             "                  Report only for selected pids.\n"
             "    --sort key1,key2,...\n"
@@ -272,7 +273,8 @@ class ReportCommand : public Command {
         use_branch_address_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
-        callgraph_show_callee_(true) {
+        callgraph_show_callee_(true),
+        report_fp_(nullptr) {
     compare_sample_func_t compare_sample_callback = std::bind(
         &ReportCommand::CompareSampleEntry, this, std::placeholders::_1, std::placeholders::_2);
     sample_tree_ =
@@ -289,13 +291,15 @@ class ReportCommand : public Command {
   void ProcessSampleRecord(const SampleRecord& r);
   bool ReadFeaturesFromRecordFile();
   int CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2);
-  void PrintReport();
+  bool PrintReport();
   void PrintReportContext();
   void CollectReportWidth();
   void CollectReportEntryWidth(const SampleEntry& sample);
   void PrintReportHeader();
   void PrintReportEntry(const SampleEntry& sample);
   void PrintCallGraph(const SampleEntry& sample);
+  void PrintCallGraphEntry(size_t depth, std::string prefix, const std::unique_ptr<CallChainNode>& node,
+                           uint64_t parent_period, bool last);
 
   std::string record_filename_;
   std::unique_ptr<RecordFileReader> record_file_reader_;
@@ -309,6 +313,9 @@ class ReportCommand : public Command {
   bool accumulate_callchain_;
   bool print_callgraph_;
   bool callgraph_show_callee_;
+
+  std::string report_filename_;
+  FILE* report_fp_;
 };
 
 bool ReportCommand::Run(const std::vector<std::string>& args) {
@@ -332,7 +339,9 @@ bool ReportCommand::Run(const std::vector<std::string>& args) {
   ReadSampleTreeFromRecordFile();
 
   // 3. Show collected information.
-  PrintReport();
+  if (!PrintReport()) {
+    return false;
+  }
 
   return true;
 }
@@ -386,6 +395,11 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
 
     } else if (args[i] == "--no-demangle") {
       demangle = false;
+    } else if (args[i] == "-o") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      report_filename_ = args[i];
 
     } else if (args[i] == "--pids" || args[i] == "--tids") {
       if (!NextArgumentOrError(args, &i)) {
@@ -641,13 +655,29 @@ int ReportCommand::CompareSampleEntry(const SampleEntry& sample1, const SampleEn
   return 0;
 }
 
-void ReportCommand::PrintReport() {
+bool ReportCommand::PrintReport() {
+  std::unique_ptr<FILE, decltype(&fclose)> file_handler(nullptr, fclose);
+  if (report_filename_.empty()) {
+    report_fp_ = stdout;
+  } else {
+    report_fp_ = fopen(report_filename_.c_str(), "w");
+    if (report_fp_ == nullptr) {
+      PLOG(ERROR) << "failed to open file " << report_filename_;
+      return false;
+    }
+    file_handler.reset(report_fp_);
+  }
   PrintReportContext();
   CollectReportWidth();
   PrintReportHeader();
   sample_tree_->VisitAllSamples(
       std::bind(&ReportCommand::PrintReportEntry, this, std::placeholders::_1));
-  fflush(stdout);
+  fflush(report_fp_);
+  if (ferror(report_fp_) != 0) {
+    PLOG(ERROR) << "print report failed";
+    return false;
+  }
+  return true;
 }
 
 void ReportCommand::PrintReportContext() {
@@ -660,11 +690,11 @@ void ReportCommand::PrintReportContext() {
         android::base::StringPrintf("(type %u, config %llu)", event_attr_.type, event_attr_.config);
   }
   if (!record_cmdline_.empty()) {
-    printf("Cmdline: %s\n", record_cmdline_.c_str());
+    fprintf(report_fp_, "Cmdline: %s\n", record_cmdline_.c_str());
   }
-  printf("Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(),
-         event_type_name.c_str());
-  printf("Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod());
+  fprintf(report_fp_, "Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(),
+          event_type_name.c_str());
+  fprintf(report_fp_, "Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod());
 }
 
 void ReportCommand::CollectReportWidth() {
@@ -682,9 +712,9 @@ void ReportCommand::PrintReportHeader() {
   for (size_t i = 0; i < displayable_items_.size(); ++i) {
     auto& item = displayable_items_[i];
     if (i != displayable_items_.size() - 1) {
-      printf("%-*s  ", static_cast<int>(item->Width()), item->Name().c_str());
+      fprintf(report_fp_, "%-*s  ", static_cast<int>(item->Width()), item->Name().c_str());
     } else {
-      printf("%s\n", item->Name().c_str());
+      fprintf(report_fp_, "%s\n", item->Name().c_str());
     }
   }
 }
@@ -693,9 +723,9 @@ void ReportCommand::PrintReportEntry(const SampleEntry& sample) {
   for (size_t i = 0; i < displayable_items_.size(); ++i) {
     auto& item = displayable_items_[i];
     if (i != displayable_items_.size() - 1) {
-      printf("%-*s  ", static_cast<int>(item->Width()), item->Show(sample).c_str());
+      fprintf(report_fp_, "%-*s  ", static_cast<int>(item->Width()), item->Show(sample).c_str());
     } else {
-      printf("%s\n", item->Show(sample).c_str());
+      fprintf(report_fp_, "%s\n", item->Show(sample).c_str());
     }
   }
   if (print_callgraph_) {
@@ -703,15 +733,26 @@ void ReportCommand::PrintReportEntry(const SampleEntry& sample) {
   }
 }
 
-static void PrintCallGraphEntry(size_t depth, std::string prefix,
-                                const std::unique_ptr<CallChainNode>& node, uint64_t parent_period,
-                                bool last) {
+void ReportCommand::PrintCallGraph(const SampleEntry& sample) {
+  std::string prefix = "       ";
+  fprintf(report_fp_, "%s|\n", prefix.c_str());
+  fprintf(report_fp_, "%s-- %s\n", prefix.c_str(), sample.symbol->DemangledName());
+  prefix.append(3, ' ');
+  for (size_t i = 0; i < sample.callchain.children.size(); ++i) {
+    PrintCallGraphEntry(1, prefix, sample.callchain.children[i], sample.callchain.children_period,
+                        (i + 1 == sample.callchain.children.size()));
+  }
+}
+
+void ReportCommand::PrintCallGraphEntry(size_t depth, std::string prefix,
+                                        const std::unique_ptr<CallChainNode>& node,
+                                        uint64_t parent_period, bool last) {
   if (depth > 20) {
     LOG(WARNING) << "truncated callgraph at depth " << depth;
     return;
   }
   prefix += "|";
-  printf("%s\n", prefix.c_str());
+  fprintf(report_fp_, "%s\n", prefix.c_str());
   if (last) {
     prefix.back() = ' ';
   }
@@ -720,10 +761,10 @@ static void PrintCallGraphEntry(size_t depth, std::string prefix,
     double percentage = 100.0 * (node->period + node->children_period) / parent_period;
     percentage_s = android::base::StringPrintf("--%.2lf%%-- ", percentage);
   }
-  printf("%s%s%s\n", prefix.c_str(), percentage_s.c_str(), node->chain[0]->symbol->DemangledName());
+  fprintf(report_fp_, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(), node->chain[0]->symbol->DemangledName());
   prefix.append(percentage_s.size(), ' ');
   for (size_t i = 1; i < node->chain.size(); ++i) {
-    printf("%s%s\n", prefix.c_str(), node->chain[i]->symbol->DemangledName());
+    fprintf(report_fp_, "%s%s\n", prefix.c_str(), node->chain[i]->symbol->DemangledName());
   }
 
   for (size_t i = 0; i < node->children.size(); ++i) {
@@ -732,17 +773,6 @@ static void PrintCallGraphEntry(size_t depth, std::string prefix,
   }
 }
 
-void ReportCommand::PrintCallGraph(const SampleEntry& sample) {
-  std::string prefix = "       ";
-  printf("%s|\n", prefix.c_str());
-  printf("%s-- %s\n", prefix.c_str(), sample.symbol->DemangledName());
-  prefix.append(3, ' ');
-  for (size_t i = 0; i < sample.callchain.children.size(); ++i) {
-    PrintCallGraphEntry(1, prefix, sample.callchain.children[i], sample.callchain.children_period,
-                        (i + 1 == sample.callchain.children.size()));
-  }
-}
-
 __attribute__((constructor)) static void RegisterReportCommand() {
   RegisterCommand("report", [] { return std::unique_ptr<Command>(new ReportCommand()); });
 }
index 4feac19..afdd9fd 100644 (file)
 
 #include <gtest/gtest.h>
 
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+
 #include "command.h"
 #include "event_selection_set.h"
+#include "get_test_data.h"
+#include "read_apk.h"
 
 static std::unique_ptr<Command> RecordCmd() {
   return CreateCommandInstance("record");
@@ -102,3 +107,13 @@ TEST(report_cmd, dwarf_callgraph) {
         << "This test does nothing as dwarf callchain sampling is not supported on this device.";
   }
 }
+
+TEST(report_cmd, report_symbols_of_nativelib_in_apk) {
+  TemporaryFile tmp_file;
+  ASSERT_TRUE(ReportCmd()->Run({"-i", GetTestData(NATIVELIB_IN_APK_PERF_DATA),
+                                "--symfs", GetTestDataDir(), "-o", tmp_file.path}));
+  std::string content;
+  ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &content));
+  ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos);
+  ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
+}
index 3444806..b45e02f 100644 (file)
@@ -49,6 +49,16 @@ TEST(stat_cmd, event_modifier) {
   ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-cycles:u,sched:sched_switch:k", "sleep", "1"}));
 }
 
+void CreateProcesses(size_t count, std::vector<std::unique_ptr<Workload>>* workloads) {
+  workloads->clear();
+  for (size_t i = 0; i < count; ++i) {
+    auto workload = Workload::CreateWorkload({"sleep", "1"});
+    ASSERT_TRUE(workload != nullptr);
+    ASSERT_TRUE(workload->Start());
+    workloads->push_back(std::move(workload));
+  }
+}
+
 TEST(stat_cmd, existing_processes) {
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(2, &workloads);
index 6397eaa..9c33667 100644 (file)
@@ -26,6 +26,7 @@
 #include <android-base/logging.h>
 
 #include "environment.h"
+#include "read_apk.h"
 #include "read_elf.h"
 #include "utils.h"
 
@@ -199,9 +200,14 @@ bool Dso::Load() {
     case DSO_KERNEL_MODULE:
       result = LoadKernelModule();
       break;
-    case DSO_ELF_FILE:
-      result = LoadElfFile();
+    case DSO_ELF_FILE: {
+      if (std::get<0>(SplitUrlInApk(path_))) {
+        result = LoadEmbeddedElfFile();
+      } else {
+        result = LoadElfFile();
+      }
       break;
+    }
   }
   if (result) {
     std::sort(symbols_.begin(), symbols_.end(), SymbolComparator());
@@ -305,6 +311,16 @@ bool Dso::LoadElfFile() {
   return loaded;
 }
 
+bool Dso::LoadEmbeddedElfFile() {
+  std::string path = GetAccessiblePath();
+  BuildId build_id = GetExpectedBuildId(path);
+  auto tuple = SplitUrlInApk(path);
+  CHECK(std::get<0>(tuple));
+  return ParseSymbolsFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id,
+                                 std::bind(ElfFileSymbolCallback, std::placeholders::_1,
+                                           this, SymbolFilterForDso));
+}
+
 void Dso::InsertSymbol(const Symbol& symbol) {
   symbols_.push_back(symbol);
 }
index a140e5e..9697319 100644 (file)
@@ -93,6 +93,7 @@ struct Dso {
   bool LoadKernel();
   bool LoadKernelModule();
   bool LoadElfFile();
+  bool LoadEmbeddedElfFile();
   void InsertSymbol(const Symbol& symbol);
   void FixupSymbolLength();
 
index 038e577..df731f1 100644 (file)
@@ -211,6 +211,7 @@ bool EventSelectionSet::OpenEventFiles(const std::vector<pid_t>& threads,
       for (auto& cpu : cpus) {
         auto event_fd = EventFd::OpenEventFile(selection.event_attr, tid, cpu);
         if (event_fd != nullptr) {
+          LOG(VERBOSE) << "OpenEventFile for tid " << tid << ", cpu " << cpu;
           selection.event_fds.push_back(std::move(event_fd));
           ++open_per_thread;
         }
index 313da04..bceedc0 100644 (file)
 
 #include <string>
 
+#include "build_id.h"
+
 std::string GetTestData(const std::string& filename);
+const std::string& GetTestDataDir();
+
+static const std::string APK_FILE = "data/app/com.example.hellojni-1/base.apk";
+static const std::string NATIVELIB_IN_APK = "lib/arm64-v8a/libhello-jni.so";
+static const std::string NATIVELIB_IN_APK_PERF_DATA = "has_embedded_native_libs_apk_perf.data";
+
+constexpr size_t NATIVELIB_OFFSET_IN_APK = 0x8000;
+constexpr size_t NATIVELIB_SIZE_IN_APK = 0x15d8;
+
+static BuildId elf_file_build_id("7600329e31058e12b145d153ef27cd40e1a5f7b9");
+
+static BuildId native_lib_build_id("b46f51cb9c4b71fb08a2fdbefc2c187894f14008");
 
 #endif  // SIMPLE_PERF_GET_TEST_DATA_H_
index 04412ea..5e463ab 100644 (file)
@@ -45,3 +45,7 @@ int main(int argc, char** argv) {
 std::string GetTestData(const std::string& filename) {
   return testdata_dir + filename;
 }
+
+const std::string& GetTestDataDir() {
+  return testdata_dir;
+}
index 7fdae42..75313a5 100644 (file)
@@ -24,6 +24,7 @@
 #include "command.h"
 
 static std::map<std::string, android::base::LogSeverity> log_severity_map = {
+    {"verbose", android::base::VERBOSE},
     {"debug", android::base::DEBUG},
     {"warning", android::base::WARNING},
     {"error", android::base::ERROR},
@@ -35,33 +36,32 @@ int main(int argc, char** argv) {
   std::vector<std::string> args;
   android::base::LogSeverity log_severity = android::base::WARNING;
 
-  if (argc == 1) {
-    args.push_back("help");
-  } else {
-    for (int i = 1; i < argc; ++i) {
-      if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
-        args.insert(args.begin(), "help");
-      } else if (strcmp(argv[i], "--log") == 0) {
-        if (i + 1 < argc) {
-          ++i;
-          auto it = log_severity_map.find(argv[i]);
-          if (it != log_severity_map.end()) {
-            log_severity = it->second;
-          } else {
-            LOG(ERROR) << "Unknown log severity: " << argv[i];
-            return 1;
-          }
+  for (int i = 1; i < argc; ++i) {
+    if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
+      args.insert(args.begin(), "help");
+    } else if (strcmp(argv[i], "--log") == 0) {
+      if (i + 1 < argc) {
+        ++i;
+        auto it = log_severity_map.find(argv[i]);
+        if (it != log_severity_map.end()) {
+          log_severity = it->second;
         } else {
-          LOG(ERROR) << "Missing argument for --log option.\n";
+          LOG(ERROR) << "Unknown log severity: " << argv[i];
           return 1;
         }
       } else {
-        args.push_back(argv[i]);
+        LOG(ERROR) << "Missing argument for --log option.\n";
+        return 1;
       }
+    } else {
+      args.push_back(argv[i]);
     }
   }
   android::base::ScopedLogSeverity severity(log_severity);
 
+  if (args.empty()) {
+    args.push_back("help");
+  }
   std::unique_ptr<Command> command = CreateCommandInstance(args[0]);
   if (command == nullptr) {
     LOG(ERROR) << "malformed command line: unknown command " << args[0];
index 270b305..55de844 100644 (file)
@@ -24,8 +24,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <map>
-
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <ziparchive/zip_archive.h>
 #include "read_elf.h"
 #include "utils.h"
 
-bool IsValidJarOrApkPath(const std::string& filename) {
-  static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04 };
-  if (!IsRegularFile(filename)) {
-    return false;
-  }
-  std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE;
-  FILE* fp = fopen(filename.c_str(), mode.c_str());
-  if (fp == nullptr) {
-    return false;
-  }
-  char buf[4];
-  if (fread(buf, 4, 1, fp) != 1) {
-    fclose(fp);
-    return false;
-  }
-  fclose(fp);
-  return memcmp(buf, zip_preamble, 4) == 0;
-}
-
 class ArchiveHelper {
  public:
-  explicit ArchiveHelper(int fd) : valid_(false) {
+  explicit ArchiveHelper(int fd, const std::string& debug_filename) : valid_(false) {
     int rc = OpenArchiveFd(fd, "", &handle_, false);
     if (rc == 0) {
       valid_ = true;
+    } else {
+      LOG(ERROR) << "Failed to open archive " << debug_filename << ": " << ErrorCodeString(rc);
     }
   }
   ~ArchiveHelper() {
@@ -73,41 +54,33 @@ class ArchiveHelper {
   bool valid_;
 };
 
-// First component of pair is APK file path, second is offset into APK
-typedef std::pair<std::string, size_t> ApkOffset;
-
-class ApkInspectorImpl {
- public:
-  EmbeddedElf *FindElfInApkByMmapOffset(const std::string& apk_path,
-                                        size_t mmap_offset);
- private:
-  std::vector<EmbeddedElf> embedded_elf_files_;
-  // Value is either 0 (no elf) or 1-based slot in array above.
-  std::map<ApkOffset, uint32_t> cache_;
-};
+std::map<ApkInspector::ApkOffset, std::unique_ptr<EmbeddedElf>> ApkInspector::embedded_elf_cache_;
 
-EmbeddedElf *ApkInspectorImpl::FindElfInApkByMmapOffset(const std::string& apk_path,
-                                                        size_t mmap_offset)
-{
+EmbeddedElf* ApkInspector::FindElfInApkByOffset(const std::string& apk_path, off64_t file_offset) {
   // Already in cache?
-  ApkOffset ami(apk_path, mmap_offset);
-  auto it = cache_.find(ami);
-  if (it != cache_.end()) {
-    uint32_t idx = it->second;
-    return (idx ? &embedded_elf_files_[idx-1] : nullptr);
+  ApkOffset ami(apk_path, file_offset);
+  auto it = embedded_elf_cache_.find(ami);
+  if (it != embedded_elf_cache_.end()) {
+    return it->second.get();
   }
-  cache_[ami] = 0u;
+  std::unique_ptr<EmbeddedElf> elf = FindElfInApkByOffsetWithoutCache(apk_path, file_offset);
+  EmbeddedElf* result = elf.get();
+  embedded_elf_cache_[ami] = std::move(elf);
+  return result;
+}
 
+std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByOffsetWithoutCache(const std::string& apk_path,
+                                                                            off64_t file_offset) {
   // Crack open the apk(zip) file and take a look.
-  if (! IsValidJarOrApkPath(apk_path)) {
+  if (!IsValidApkPath(apk_path)) {
     return nullptr;
   }
   FileHelper fhelper(apk_path.c_str());
-  if (fhelper.fd() == -1) {
+  if (!fhelper) {
     return nullptr;
   }
 
-  ArchiveHelper ahelper(fhelper.fd());
+  ArchiveHelper ahelper(fhelper.fd(), apk_path);
   if (!ahelper.valid()) {
     return nullptr;
   }
@@ -124,11 +97,10 @@ EmbeddedElf *ApkInspectorImpl::FindElfInApkByMmapOffset(const std::string& apk_p
   ZipString zname;
   bool found = false;
   int zrc;
-  off64_t mmap_off64 = mmap_offset;
   while ((zrc = Next(iteration_cookie, &zentry, &zname)) == 0) {
     if (zentry.method == kCompressStored &&
-        mmap_off64 >= zentry.offset &&
-        mmap_off64 < zentry.offset + zentry.uncompressed_length) {
+        file_offset >= zentry.offset &&
+        file_offset < zentry.offset + zentry.uncompressed_length) {
       // Found.
       found = true;
       break;
@@ -154,26 +126,87 @@ EmbeddedElf *ApkInspectorImpl::FindElfInApkByMmapOffset(const std::string& apk_p
   }
 
   // Elf found: add EmbeddedElf to vector, update cache.
-  EmbeddedElf ee(apk_path, entry_name, zentry.offset, zentry.uncompressed_length);
-  embedded_elf_files_.push_back(ee);
-  unsigned idx = embedded_elf_files_.size();
-  cache_[ami] = idx;
-  return &embedded_elf_files_[idx-1];
+  return std::unique_ptr<EmbeddedElf>(new EmbeddedElf(apk_path, entry_name, zentry.offset,
+                                                      zentry.uncompressed_length));
 }
 
-// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+std::unique_ptr<EmbeddedElf> ApkInspector::FindElfInApkByName(const std::string& apk_path,
+                                                              const std::string& elf_filename) {
+  if (!IsValidApkPath(apk_path)) {
+    return nullptr;
+  }
+  FileHelper fhelper(apk_path.c_str());
+  if (!fhelper) {
+    return nullptr;
+  }
+  ArchiveHelper ahelper(fhelper.fd(), apk_path);
+  if (!ahelper.valid()) {
+    return nullptr;
+  }
+  ZipArchiveHandle& handle = ahelper.archive_handle();
+  ZipEntry zentry;
+  int32_t rc = FindEntry(handle, ZipString(elf_filename.c_str()), &zentry);
+  if (rc != 0) {
+    LOG(ERROR) << "failed to find " << elf_filename << " in " << apk_path
+        << ": " << ErrorCodeString(rc);
+    return nullptr;
+  }
+  if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) {
+    LOG(ERROR) << "shared library " << elf_filename << " in " << apk_path << " is compressed";
+    return nullptr;
+  }
+  return std::unique_ptr<EmbeddedElf>(new EmbeddedElf(apk_path, elf_filename, zentry.offset,
+                                                  zentry.uncompressed_length));
+}
 
-ApkInspector::ApkInspector()
-    : impl_(new ApkInspectorImpl())
-{
+bool IsValidApkPath(const std::string& apk_path) {
+  static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04 };
+  if (!IsRegularFile(apk_path)) {
+    return false;
+  }
+  std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE;
+  FILE* fp = fopen(apk_path.c_str(), mode.c_str());
+  if (fp == nullptr) {
+    return false;
+  }
+  char buf[4];
+  if (fread(buf, 4, 1, fp) != 1) {
+    fclose(fp);
+    return false;
+  }
+  fclose(fp);
+  return memcmp(buf, zip_preamble, 4) == 0;
+}
+
+// Refer file in apk in compliance with http://developer.android.com/reference/java/net/JarURLConnection.html.
+std::string GetUrlInApk(const std::string& apk_path, const std::string& elf_filename) {
+  return apk_path + "!/" + elf_filename;
 }
 
-ApkInspector::~ApkInspector()
-{
+std::tuple<bool, std::string, std::string> SplitUrlInApk(const std::string& path) {
+  size_t pos = path.find("!/");
+  if (pos == std::string::npos) {
+    return std::make_tuple(false, "", "");
+  }
+  return std::make_tuple(true, path.substr(0, pos), path.substr(pos + 2));
 }
 
-EmbeddedElf *ApkInspector::FindElfInApkByMmapOffset(const std::string& apk_path,
-                                                    size_t mmap_offset)
-{
-  return impl_->FindElfInApkByMmapOffset(apk_path, mmap_offset);
+bool GetBuildIdFromApkFile(const std::string& apk_path, const std::string& elf_filename,
+                           BuildId* build_id) {
+  std::unique_ptr<EmbeddedElf> ee = ApkInspector::FindElfInApkByName(apk_path, elf_filename);
+  if (ee == nullptr) {
+    return false;
+  }
+  return GetBuildIdFromEmbeddedElfFile(apk_path, ee->entry_offset(), ee->entry_size(), build_id);
+}
+
+bool ParseSymbolsFromApkFile(const std::string& apk_path, const std::string& elf_filename,
+                             const BuildId& expected_build_id,
+                             std::function<void(const ElfFileSymbol&)> callback) {
+  std::unique_ptr<EmbeddedElf> ee = ApkInspector::FindElfInApkByName(apk_path, elf_filename);
+  if (ee == nullptr) {
+    return false;
+  }
+  return ParseSymbolsFromEmbeddedElfFile(apk_path, ee->entry_offset(), ee->entry_size(),
+                                         expected_build_id, callback);
 }
index c35cac7..89c051a 100644 (file)
 #ifndef SIMPLE_PERF_READ_APK_H_
 #define SIMPLE_PERF_READ_APK_H_
 
+#include <map>
 #include <memory>
 #include <string>
+#include <tuple>
 
-#include <android-base/file.h>
-
-// Exposed for unit testing
-bool IsValidJarOrApkPath(const std::string& filename);
+#include "read_elf.h"
 
 // Container for info an on ELF file embedded into an APK file
 class EmbeddedElf {
@@ -64,36 +63,38 @@ class EmbeddedElf {
   uint32_t entry_size_;  // size of ELF file in zip
 };
 
-struct EmbeddedElfComparator {
-  bool operator()(const EmbeddedElf& ee1, const EmbeddedElf& ee2) {
-    int res1 = ee1.filepath().compare(ee2.filepath());
-    if (res1 != 0) {
-      return res1 < 0;
-    }
-    int res2 = ee1.entry_name().compare(ee2.entry_name());
-    if (res2 != 0) {
-      return res2 < 0;
-    }
-    return ee1.entry_offset() < ee2.entry_offset();
-  }
-};
-
-class ApkInspectorImpl;
-
 // APK inspector helper class
 class ApkInspector {
  public:
-  ApkInspector();
-  ~ApkInspector();
-
   // Given an APK/ZIP/JAR file and an offset into that file, if the
   // corresponding region of the APK corresponds to an uncompressed
   // ELF file, then return pertinent info on the ELF.
-  EmbeddedElf *FindElfInApkByMmapOffset(const std::string& apk_path,
-                                        size_t mmap_offset);
+  static EmbeddedElf* FindElfInApkByOffset(const std::string& apk_path, off64_t file_offset);
+  static std::unique_ptr<EmbeddedElf> FindElfInApkByName(const std::string& apk_path,
+                                                         const std::string& elf_filename);
 
  private:
-  std::unique_ptr<ApkInspectorImpl> impl_;
+  static std::unique_ptr<EmbeddedElf> FindElfInApkByOffsetWithoutCache(const std::string& apk_path,
+                                                                        off64_t file_offset);
+
+  // First component of pair is APK file path, second is offset into APK.
+  typedef std::pair<std::string, size_t> ApkOffset;
+
+  static std::map<ApkOffset, std::unique_ptr<EmbeddedElf>> embedded_elf_cache_;
 };
 
+// Export for test only.
+bool IsValidApkPath(const std::string& apk_path);
+
+std::string GetUrlInApk(const std::string& apk_path, const std::string& elf_filename);
+std::tuple<bool, std::string, std::string> SplitUrlInApk(const std::string& path);
+
+bool GetBuildIdFromApkFile(const std::string& apk_path, const std::string& elf_filename,
+                           BuildId* build_id);
+
+bool ParseSymbolsFromApkFile(const std::string& apk_path, const std::string& elf_filename,
+                             const BuildId& expected_build_id,
+                             std::function<void(const ElfFileSymbol&)> callback);
+
+
 #endif  // SIMPLE_PERF_READ_APK_H_
index 5824f4b..aeedbbe 100644 (file)
 
 #include <gtest/gtest.h>
 #include "get_test_data.h"
+#include "test_util.h"
 
-static const std::string fibjar = "fibonacci.jar";
-static const std::string jniapk = "has_embedded_native_libs.apk";
 
-TEST(read_apk, IsValidJarOrApkPath) {
-  ASSERT_FALSE(IsValidJarOrApkPath("/dev/zero"));
-  ASSERT_FALSE(IsValidJarOrApkPath(GetTestData("elf_file")));
-  ASSERT_TRUE(IsValidJarOrApkPath(GetTestData(fibjar)));
+TEST(read_apk, IsValidApkPath) {
+  ASSERT_FALSE(IsValidApkPath("/dev/zero"));
+  ASSERT_FALSE(IsValidApkPath(GetTestData("elf_file")));
+  ASSERT_TRUE(IsValidApkPath(GetTestData(APK_FILE)));
 }
 
-TEST(read_apk, CollectEmbeddedElfInfoFromApk) {
+TEST(read_apk, FindElfInApkByOffset) {
   ApkInspector inspector;
-  ASSERT_TRUE(inspector.FindElfInApkByMmapOffset("/dev/null", 0) == nullptr);
-  ASSERT_TRUE(inspector.FindElfInApkByMmapOffset(GetTestData(fibjar), 0) == nullptr);
-  ASSERT_TRUE(inspector.FindElfInApkByMmapOffset(GetTestData(jniapk), 0) == nullptr);
-  EmbeddedElf *ee1 = inspector.FindElfInApkByMmapOffset(GetTestData(jniapk), 0x91000);
-  ASSERT_TRUE(ee1 != nullptr);
-  ASSERT_EQ(ee1->entry_name(), "lib/armeabi-v7a/libframeworks_coretests_jni.so");
-  ASSERT_TRUE(ee1->entry_offset() == 593920);
-  ASSERT_TRUE(ee1->entry_size() == 13904);
+  ASSERT_TRUE(inspector.FindElfInApkByOffset("/dev/null", 0) == nullptr);
+  ASSERT_TRUE(inspector.FindElfInApkByOffset(GetTestData(APK_FILE), 0) == nullptr);
+  EmbeddedElf* ee = inspector.FindElfInApkByOffset(GetTestData(APK_FILE), 0x9000);
+  ASSERT_TRUE(ee != nullptr);
+  ASSERT_EQ(ee->entry_name(), NATIVELIB_IN_APK);
+  ASSERT_EQ(NATIVELIB_OFFSET_IN_APK, ee->entry_offset());
+  ASSERT_EQ(NATIVELIB_SIZE_IN_APK, ee->entry_size());
+}
+
+TEST(read_apk, FindElfInApkByName) {
+  ASSERT_TRUE(ApkInspector::FindElfInApkByName("/dev/null", "") == nullptr);
+  ASSERT_TRUE(ApkInspector::FindElfInApkByName(GetTestData(APK_FILE), "") == nullptr);
+  auto ee = ApkInspector::FindElfInApkByName(GetTestData(APK_FILE), NATIVELIB_IN_APK);
+  ASSERT_TRUE(ee != nullptr);
+  ASSERT_EQ(NATIVELIB_OFFSET_IN_APK, ee->entry_offset());
+  ASSERT_EQ(NATIVELIB_SIZE_IN_APK, ee->entry_size());
+}
+
+TEST(read_apk, GetBuildIdFromApkFile) {
+  BuildId build_id;
+  ASSERT_TRUE(GetBuildIdFromApkFile(GetTestData(APK_FILE), NATIVELIB_IN_APK, &build_id));
+  ASSERT_EQ(build_id, native_lib_build_id);
+}
+
+TEST(read_apk, ParseSymbolsFromApkFile) {
+  std::map<std::string, ElfFileSymbol> symbols;
+  ASSERT_TRUE(ParseSymbolsFromApkFile(GetTestData(APK_FILE), NATIVELIB_IN_APK, native_lib_build_id,
+                                      std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
+  CheckElfFileSymbols(symbols);
 }
index 03bdcc5..e66e68e 100644 (file)
@@ -19,7 +19,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
 #define ELF_NOTE_GNU "GNU"
 #define NT_GNU_BUILD_ID 3
 
-FileHelper::FileHelper(const char *filename) : fd_(-1)
-{
-  fd_ = TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_BINARY));
-}
-
-FileHelper::~FileHelper()
-{
-  if (fd_ != -1) { close(fd_); }
-}
 
 bool IsValidElfFile(int fd) {
   static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
@@ -145,62 +135,65 @@ static bool GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId* bui
   return result;
 }
 
-bool GetBuildIdFromEmbeddedElfFile(const std::string& filename,
-                                   uint64_t offsetInFile,
-                                   uint32_t sizeInFile,
-                                   BuildId* build_id) {
-  FileHelper opener(filename.c_str());
-  if (opener.fd() == -1) {
-    LOG(DEBUG) << "unable to open " << filename
-               << "to collect embedded ELF build id";
-    return false;
+struct BinaryRet {
+  llvm::object::OwningBinary<llvm::object::Binary> binary;
+  llvm::object::ObjectFile* obj;
+
+  BinaryRet() : obj(nullptr) {
   }
-  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> bufferOrErr =
-      llvm::MemoryBuffer::getOpenFileSlice(opener.fd(), filename, sizeInFile,
-                                           offsetInFile);
-  if (std::error_code EC = bufferOrErr.getError()) {
-    LOG(DEBUG) << "MemoryBuffer::getOpenFileSlice failed opening "
-               << filename << "while collecting embedded ELF build id: "
-               << EC.message();
-    return false;
+};
+
+static BinaryRet OpenObjectFile(const std::string& filename, uint64_t file_offset = 0,
+                                uint64_t file_size = 0) {
+  BinaryRet ret;
+  FileHelper fhelper(filename);
+  if (!fhelper) {
+    PLOG(DEBUG) << "failed to open " << filename;
+    return ret;
   }
-  std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get());
-  llvm::LLVMContext *context = nullptr;
-  llvm::ErrorOr<std::unique_ptr<llvm::object::Binary>> binaryOrErr =
-      llvm::object::createBinary(buffer->getMemBufferRef(), context);
-  if (std::error_code EC = binaryOrErr.getError()) {
-    LOG(DEBUG) << "llvm::object::createBinary failed opening "
-               << filename << "while collecting embedded ELF build id: "
-               << EC.message();
-    return false;
+  if (file_size == 0) {
+    file_size = GetFileSize(filename);
+    if (file_size == 0) {
+      PLOG(ERROR) << "failed to get size of file " << filename;
+      return ret;
+    }
   }
-  std::unique_ptr<llvm::object::Binary> binary = std::move(binaryOrErr.get());
-  auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary.get());
-  if (obj == nullptr) {
-    LOG(DEBUG) << "unable to cast to interpret contents of " << filename
-               << "at offset " << offsetInFile
-               << ": failed to cast to llvm::object::ObjectFile";
-    return false;
+  auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(fhelper.fd(), filename, file_size, file_offset);
+  if (!buffer_or_err) {
+    LOG(ERROR) << "failed to read " << filename << " [" << file_offset << "-" << (file_offset + file_size)
+        << "]: " << buffer_or_err.getError().message();
+    return ret;
+  }
+  auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef());
+  if (!binary_or_err) {
+    LOG(ERROR) << filename << " [" << file_offset << "-" << (file_offset + file_size)
+        << "] is not a binary file: " << binary_or_err.getError().message();
+    return ret;
+  }
+  ret.binary = llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
+                                                                std::move(buffer_or_err.get()));
+  ret.obj = llvm::dyn_cast<llvm::object::ObjectFile>(ret.binary.getBinary());
+  if (ret.obj == nullptr) {
+    LOG(ERROR) << filename << " [" << file_offset << "-" << (file_offset + file_size)
+        << "] is not an object file";
   }
-  return GetBuildIdFromObjectFile(obj, build_id);
+  return ret;
 }
 
 bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
   if (!IsValidElfPath(filename)) {
     return false;
   }
-  auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
-  if (owning_binary.getError()) {
-    PLOG(DEBUG) << "can't open file " << filename;
-    return false;
-  }
-  llvm::object::Binary* binary = owning_binary.get().getBinary();
-  auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
-  if (obj == nullptr) {
-    LOG(DEBUG) << filename << " is not an object file";
+  return GetBuildIdFromEmbeddedElfFile(filename, 0, 0, build_id);
+}
+
+bool GetBuildIdFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset,
+                                   uint32_t file_size, BuildId* build_id) {
+  BinaryRet ret = OpenObjectFile(filename, file_offset, file_size);
+  if (ret.obj == nullptr) {
     return false;
   }
-  return GetBuildIdFromObjectFile(obj, build_id);
+  return GetBuildIdFromObjectFile(ret.obj, build_id);
 }
 
 bool IsArmMappingSymbol(const char* name) {
@@ -274,31 +267,22 @@ void ParseSymbolsFromELFFile(const llvm::object::ELFFile<ELFT>* elf,
   }
 }
 
-static llvm::object::ObjectFile* GetObjectFile(
-    llvm::ErrorOr<llvm::object::OwningBinary<llvm::object::Binary>>& owning_binary,
-    const std::string& filename, const BuildId& expected_build_id) {
-  if (owning_binary.getError()) {
-    PLOG(DEBUG) << "can't open file '" << filename << "'";
-    return nullptr;
+bool MatchBuildId(llvm::object::ObjectFile* obj, const BuildId& expected_build_id,
+                  const std::string& debug_filename) {
+  if (expected_build_id.IsEmpty()) {
+    return true;
   }
-  llvm::object::Binary* binary = owning_binary.get().getBinary();
-  auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
-  if (obj == nullptr) {
-    LOG(DEBUG) << filename << " is not an object file";
-    return nullptr;
+  BuildId real_build_id;
+  if (!GetBuildIdFromObjectFile(obj, &real_build_id)) {
+    return false;
   }
-  if (!expected_build_id.IsEmpty()) {
-    BuildId real_build_id;
-    GetBuildIdFromObjectFile(obj, &real_build_id);
-    bool result = (expected_build_id == real_build_id);
-    LOG(DEBUG) << "check build id for \"" << filename << "\" (" << (result ? "match" : "mismatch")
-               << "): expected " << expected_build_id.ToString() << ", real "
-               << real_build_id.ToString();
-    if (!result) {
-      return nullptr;
-    }
+  if (expected_build_id != real_build_id) {
+    LOG(DEBUG) << "build id for " << debug_filename << " mismatch: "
+               << "expected " << expected_build_id.ToString()
+               << ", real " << real_build_id.ToString();
+    return false;
   }
-  return obj;
+  return true;
 }
 
 bool ParseSymbolsFromElfFile(const std::string& filename, const BuildId& expected_build_id,
@@ -306,18 +290,22 @@ bool ParseSymbolsFromElfFile(const std::string& filename, const BuildId& expecte
   if (!IsValidElfPath(filename)) {
     return false;
   }
-  auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
-  llvm::object::ObjectFile* obj = GetObjectFile(owning_binary, filename, expected_build_id);
-  if (obj == nullptr) {
+  return ParseSymbolsFromEmbeddedElfFile(filename, 0, 0, expected_build_id, callback);
+}
+
+bool ParseSymbolsFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset,
+                                     uint32_t file_size, const BuildId& expected_build_id,
+                                     std::function<void(const ElfFileSymbol&)> callback) {
+  BinaryRet ret = OpenObjectFile(filename, file_offset, file_size);
+  if (ret.obj == nullptr || !MatchBuildId(ret.obj, expected_build_id, filename)) {
     return false;
   }
-
-  if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
+  if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
     ParseSymbolsFromELFFile(elf->getELFFile(), callback);
-  } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
+  } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
     ParseSymbolsFromELFFile(elf->getELFFile(), callback);
   } else {
-    LOG(ERROR) << "unknown elf format in file" << filename;
+    LOG(ERROR) << "unknown elf format in file " << filename;
     return false;
   }
   return true;
@@ -347,16 +335,15 @@ bool ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename,
   if (!IsValidElfPath(filename)) {
     return false;
   }
-  auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
-  llvm::object::ObjectFile* obj = GetObjectFile(owning_binary, filename, expected_build_id);
-  if (obj == nullptr) {
+  BinaryRet ret = OpenObjectFile(filename);
+  if (ret.obj == nullptr || !MatchBuildId(ret.obj, expected_build_id, filename)) {
     return false;
   }
 
   bool result = false;
-  if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
+  if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
     result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
-  } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
+  } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
     result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
   } else {
     LOG(ERROR) << "unknown elf format in file" << filename;
index d0626b2..11dc8d8 100644 (file)
 
 bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id);
 bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id);
-bool GetBuildIdFromEmbeddedElfFile(const std::string& filename,
-                                   uint64_t offsetInFile,
-                                   uint32_t sizeInFile,
-                                   BuildId* build_id);
+bool GetBuildIdFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset,
+                                   uint32_t file_size, BuildId* build_id);
 
 // The symbol prefix used to indicate that the symbol belongs to android linker.
 static const std::string linker_prefix = "__dl_";
@@ -45,22 +43,14 @@ struct ElfFileSymbol {
 
 bool ParseSymbolsFromElfFile(const std::string& filename, const BuildId& expected_build_id,
                              std::function<void(const ElfFileSymbol&)> callback);
+bool ParseSymbolsFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset,
+                                     uint32_t file_size, const BuildId& expected_build_id,
+                                     std::function<void(const ElfFileSymbol&)> callback);
 
 bool ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename,
                                                 const BuildId& expected_build_id,
                                                 uint64_t* min_addr);
 
-// Opens file in constructor, then closes file when object is destroyed.
-class FileHelper {
- public:
-  explicit FileHelper(const char *filename);
-  ~FileHelper();
-  int fd() const { return fd_; }
-
- private:
-  int fd_;
-};
-
 // Expose the following functions for unit tests.
 bool IsArmMappingSymbol(const char* name);
 bool IsValidElfFile(int fd);
index 7a5194e..920da27 100644 (file)
 #include <map>
 #include "get_test_data.h"
 
-static const unsigned char elf_file_build_id[] = {
-    0x76, 0x00, 0x32, 0x9e, 0x31, 0x05, 0x8e, 0x12, 0xb1, 0x45,
-    0xd1, 0x53, 0xef, 0x27, 0xcd, 0x40, 0xe1, 0xa5, 0xf7, 0xb9
-};
-
 TEST(read_elf, GetBuildIdFromElfFile) {
   BuildId build_id;
   ASSERT_TRUE(GetBuildIdFromElfFile(GetTestData("elf_file"), &build_id));
   ASSERT_EQ(build_id, BuildId(elf_file_build_id));
 }
 
-static void ParseSymbol(const ElfFileSymbol& symbol, std::map<std::string, ElfFileSymbol>* symbols) {
+TEST(read_elf, GetBuildIdFromEmbeddedElfFile) {
+  BuildId build_id;
+  ASSERT_TRUE(GetBuildIdFromEmbeddedElfFile(GetTestData(APK_FILE), NATIVELIB_OFFSET_IN_APK,
+                                            NATIVELIB_SIZE_IN_APK, &build_id));
+  ASSERT_EQ(build_id, native_lib_build_id);
+}
+
+void ParseSymbol(const ElfFileSymbol& symbol, std::map<std::string, ElfFileSymbol>* symbols) {
   (*symbols)[symbol.name] = symbol;
 }
 
-static void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols) {
+void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols) {
   auto pos = symbols.find("GlobalVar");
   ASSERT_NE(pos, symbols.end());
   ASSERT_FALSE(pos->second.is_func);
@@ -47,28 +49,34 @@ static void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symb
 }
 
 TEST(read_elf, parse_symbols_from_elf_file_with_correct_build_id) {
-  BuildId build_id(elf_file_build_id);
   std::map<std::string, ElfFileSymbol> symbols;
-  ASSERT_TRUE(ParseSymbolsFromElfFile(GetTestData("elf_file"), build_id,
+  ASSERT_TRUE(ParseSymbolsFromElfFile(GetTestData("elf_file"), elf_file_build_id,
                                       std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
   CheckElfFileSymbols(symbols);
 }
 
 TEST(read_elf, parse_symbols_from_elf_file_without_build_id) {
-  BuildId build_id;
   std::map<std::string, ElfFileSymbol> symbols;
-  ASSERT_TRUE(ParseSymbolsFromElfFile(GetTestData("elf_file"), build_id,
+  ASSERT_TRUE(ParseSymbolsFromElfFile(GetTestData("elf_file"), BuildId(),
                                       std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
   CheckElfFileSymbols(symbols);
 }
 
 TEST(read_elf, parse_symbols_from_elf_file_with_wrong_build_id) {
-  BuildId build_id("wrong_build_id");
+  BuildId build_id("01010101010101010101");
   std::map<std::string, ElfFileSymbol> symbols;
   ASSERT_FALSE(ParseSymbolsFromElfFile(GetTestData("elf_file"), build_id,
                                        std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
 }
 
+TEST(read_elf, ParseSymbolsFromEmbeddedElfFile) {
+  std::map<std::string, ElfFileSymbol> symbols;
+  ASSERT_TRUE(ParseSymbolsFromEmbeddedElfFile(GetTestData(APK_FILE), NATIVELIB_OFFSET_IN_APK,
+                                              NATIVELIB_SIZE_IN_APK, native_lib_build_id,
+                                              std::bind(ParseSymbol, std::placeholders::_1, &symbols)));
+  CheckElfFileSymbols(symbols);
+}
+
 TEST(read_elf, arm_mapping_symbol) {
   ASSERT_TRUE(IsArmMappingSymbol("$a"));
   ASSERT_FALSE(IsArmMappingSymbol("$b"));
index 26bc588..660634c 100644 (file)
@@ -73,25 +73,7 @@ size_t SampleId::CreateContent(const perf_event_attr& attr) {
   sample_id_all = attr.sample_id_all;
   sample_type = attr.sample_type;
   // Other data are not necessary. TODO: Set missing SampleId data.
-  size_t size = 0;
-  if (sample_id_all) {
-    if (sample_type & PERF_SAMPLE_TID) {
-      size += sizeof(PerfSampleTidType);
-    }
-    if (sample_type & PERF_SAMPLE_TIME) {
-      size += sizeof(PerfSampleTimeType);
-    }
-    if (sample_type & PERF_SAMPLE_ID) {
-      size += sizeof(PerfSampleIdType);
-    }
-    if (sample_type & PERF_SAMPLE_STREAM_ID) {
-      size += sizeof(PerfSampleStreamIdType);
-    }
-    if (sample_type & PERF_SAMPLE_CPU) {
-      size += sizeof(PerfSampleCpuType);
-    }
-  }
-  return size;
+  return Size();
 }
 
 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) {
@@ -161,6 +143,28 @@ void SampleId::Dump(size_t indent) const {
   }
 }
 
+size_t SampleId::Size() const {
+  size_t size = 0;
+  if (sample_id_all) {
+    if (sample_type & PERF_SAMPLE_TID) {
+      size += sizeof(PerfSampleTidType);
+    }
+    if (sample_type & PERF_SAMPLE_TIME) {
+      size += sizeof(PerfSampleTimeType);
+    }
+    if (sample_type & PERF_SAMPLE_ID) {
+      size += sizeof(PerfSampleIdType);
+    }
+    if (sample_type & PERF_SAMPLE_STREAM_ID) {
+      size += sizeof(PerfSampleStreamIdType);
+    }
+    if (sample_type & PERF_SAMPLE_CPU) {
+      size += sizeof(PerfSampleCpuType);
+    }
+  }
+  return size;
+}
+
 Record::Record() {
   memset(&header, 0, sizeof(header));
 }
@@ -202,6 +206,10 @@ std::vector<char> MmapRecord::BinaryFormat() const {
   return buf;
 }
 
+void MmapRecord::AdjustSizeBasedOnData() {
+  header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
+}
+
 void MmapRecord::DumpData(size_t indent) const {
   PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
                 data.tid, data.addr, data.len);
@@ -230,6 +238,10 @@ std::vector<char> Mmap2Record::BinaryFormat() const {
   return buf;
 }
 
+void Mmap2Record::AdjustSizeBasedOnData() {
+  header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
+}
+
 void Mmap2Record::DumpData(size_t indent) const {
   PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
                 data.tid, data.addr, data.len);
@@ -437,7 +449,8 @@ std::vector<char> SampleRecord::BinaryFormat() const {
 
 void SampleRecord::AdjustSizeBasedOnData() {
   size_t size = BinaryFormat().size();
-  LOG(DEBUG) << "SampleRecord size is changed from " << header.size << " to " << size;
+  LOG(DEBUG) << "Record (type " << RecordTypeToString(header.type) << ") size is changed from "
+      << header.size << " to " << size;
   header.size = size;
 }
 
@@ -632,18 +645,6 @@ MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_
   return record;
 }
 
-void UpdateMmapRecord(MmapRecord *record, const std::string& new_filename, uint64_t new_pgoff)
-{
-  size_t new_filename_size = ALIGN(new_filename.size() + 1, 8);
-  size_t old_filename_size = ALIGN(record->filename.size() + 1, 8);
-  record->data.pgoff = new_pgoff;
-  record->filename = new_filename;
-  if (new_filename_size > old_filename_size)
-    record->header.size += (new_filename_size - old_filename_size);
-  else if (new_filename_size < old_filename_size)
-    record->header.size += (old_filename_size - new_filename_size);
-}
-
 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
                             const std::string& comm) {
   CommRecord record;
index 26e4599..a94a917 100644 (file)
@@ -125,6 +125,7 @@ struct SampleId {
   // Write the binary format of sample_id to the buffer pointed by p.
   void WriteToBinaryFormat(char*& p) const;
   void Dump(size_t indent) const;
+  size_t Size() const;
 };
 
 // Usually one record contains the following three parts in order in binary format:
@@ -173,6 +174,7 @@ struct MmapRecord : public Record {
 
   MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader);
   std::vector<char> BinaryFormat() const override;
+  void AdjustSizeBasedOnData();
 
  protected:
   void DumpData(size_t indent) const override;
@@ -197,6 +199,7 @@ struct Mmap2Record : public Record {
 
   Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader);
   std::vector<char> BinaryFormat() const override;
+  void AdjustSizeBasedOnData();
 
  protected:
   void DumpData(size_t indent) const override;
@@ -357,7 +360,6 @@ std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp
 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
                             uint64_t addr, uint64_t len, uint64_t pgoff,
                             const std::string& filename);
-void UpdateMmapRecord(MmapRecord *record, const std::string& new_filename, uint64_t new_pgoff);
 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
                             const std::string& comm);
 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
index 34155a3..734a48b 100644 (file)
  * limitations under the License.
  */
 
+#include <map>
+
+#include "read_elf.h"
 #include "workload.h"
 
-static void CreateProcesses(size_t count, std::vector<std::unique_ptr<Workload>>* workloads) {
-  workloads->clear();
-  for (size_t i = 0; i < count; ++i) {
-    auto workload = Workload::CreateWorkload({"sleep", "1"});
-    ASSERT_TRUE(workload != nullptr);
-    ASSERT_TRUE(workload->Start());
-    workloads->push_back(std::move(workload));
-  }
-}
+void CreateProcesses(size_t count, std::vector<std::unique_ptr<Workload>>* workloads);
+
+void ParseSymbol(const ElfFileSymbol& symbol, std::map<std::string, ElfFileSymbol>* symbols);
+void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols);
diff --git a/simpleperf/testdata/data/app/com.example.hellojni-1/base.apk b/simpleperf/testdata/data/app/com.example.hellojni-1/base.apk
new file mode 100644 (file)
index 0000000..c757e9e
Binary files /dev/null and b/simpleperf/testdata/data/app/com.example.hellojni-1/base.apk differ
diff --git a/simpleperf/testdata/fibonacci.jar b/simpleperf/testdata/fibonacci.jar
deleted file mode 100644 (file)
index df57e40..0000000
Binary files a/simpleperf/testdata/fibonacci.jar and /dev/null differ
diff --git a/simpleperf/testdata/has_embedded_native_libs.apk b/simpleperf/testdata/has_embedded_native_libs.apk
deleted file mode 100644 (file)
index 2a1924c..0000000
Binary files a/simpleperf/testdata/has_embedded_native_libs.apk and /dev/null differ
diff --git a/simpleperf/testdata/has_embedded_native_libs_apk_perf.data b/simpleperf/testdata/has_embedded_native_libs_apk_perf.data
new file mode 100644 (file)
index 0000000..f85c9d3
Binary files /dev/null and b/simpleperf/testdata/has_embedded_native_libs_apk_perf.data differ
index eabad29..2e68767 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -26,6 +27,7 @@
 #include <algorithm>
 #include <string>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 
 void OneTimeFreeAllocator::Clear() {
@@ -52,6 +54,19 @@ const char* OneTimeFreeAllocator::AllocateString(const std::string& s) {
   return result;
 }
 
+FileHelper::FileHelper() : fd_(-1) {
+}
+
+FileHelper::FileHelper(const std::string& filename) {
+  fd_ = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_BINARY));
+}
+
+FileHelper::~FileHelper() {
+  if (fd_ != -1) {
+    close(fd_);
+  }
+}
+
 void PrintIndented(size_t indent, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
@@ -114,3 +129,11 @@ bool IsRegularFile(const std::string& filename) {
   }
   return false;
 }
+
+uint64_t GetFileSize(const std::string& filename) {
+  struct stat st;
+  if (stat(filename.c_str(), &st) == 0) {
+    return static_cast<uint64_t>(st.st_size);
+  }
+  return 0;
+}
index 2ce0726..6581a76 100644 (file)
@@ -22,6 +22,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/macros.h>
+
 #define ALIGN(value, alignment) (((value) + (alignment)-1) & ~((alignment)-1))
 
 #ifdef _WIN32
@@ -52,6 +54,26 @@ class OneTimeFreeAllocator {
   char* end_;
 };
 
+class FileHelper {
+ public:
+  FileHelper();
+  explicit FileHelper(const std::string& filename);
+  ~FileHelper();
+
+  explicit operator bool() const {
+    return fd_ != -1;
+  }
+
+  int fd() const {
+    return fd_;
+  }
+
+ private:
+  int fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileHelper);
+};
+
 template <class T>
 void MoveFromBinaryFormat(T& data, const char*& p) {
   data = *reinterpret_cast<const T*>(p);
@@ -66,5 +88,6 @@ void GetEntriesInDir(const std::string& dirpath, std::vector<std::string>* files
                      std::vector<std::string>* subdirs);
 bool IsDir(const std::string& dirpath);
 bool IsRegularFile(const std::string& filename);
+uint64_t GetFileSize(const std::string& filename);
 
 #endif  // SIMPLE_PERF_UTILS_H_