OSDN Git Service

simpleperf: record kernel symbols in perf.data.
authorYabin Cui <yabinc@google.com>
Wed, 25 May 2016 21:08:05 +0000 (14:08 -0700)
committerYabin Cui <yabinc@google.com>
Tue, 31 May 2016 21:16:14 +0000 (14:16 -0700)
To better support kernel profiling, record kernel symbols in perf.data
when necessary. An option --no-dump-kernel-symbols is added in
record command to always avoid recording kernel symbols.
The way to handle all zero /proc/modules and /proc/kallsyms is
improved. Add Better support in finding symbols for kernel modules.

Bug: 27403614
Change-Id: I470151c54f8a45ad1c101c1b94490e33d7fd7485

22 files changed:
simpleperf/Android.mk
simpleperf/cmd_record.cpp
simpleperf/cmd_record_test.cpp
simpleperf/cmd_report.cpp
simpleperf/cmd_report_test.cpp
simpleperf/dso.cpp
simpleperf/dso.h
simpleperf/environment.cpp
simpleperf/environment.h
simpleperf/environment_test.cpp
simpleperf/get_test_data.h
simpleperf/nonlinux_support/nonlinux_support.cpp
simpleperf/record.cpp
simpleperf/record.h
simpleperf/record_equal_test.h
simpleperf/record_file_reader.cpp
simpleperf/testdata/perf_with_kernel_symbol.data [new file with mode: 0644]
simpleperf/thread_tree.cpp
simpleperf/thread_tree.h
simpleperf/utils.cpp
simpleperf/utils.h
simpleperf/utils_test.cpp [new file with mode: 0644]

index 842991e..ebd0aa9 100644 (file)
@@ -192,6 +192,7 @@ simpleperf_unit_test_src_files := \
   read_elf_test.cpp \
   record_test.cpp \
   sample_tree_test.cpp \
+  utils_test.cpp \
 
 simpleperf_unit_test_src_files_linux := \
   cmd_dumprecord_test.cpp \
index f3218d4..83f60e7 100644 (file)
@@ -25,6 +25,7 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/file.h>
 #include <android-base/strings.h>
 
 #include "command.h"
@@ -53,65 +54,66 @@ static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
 };
 
 static volatile bool signaled;
-static void signal_handler(int) {
-  signaled = true;
-}
+static void signal_handler(int) { signaled = true; }
 
 class RecordCommand : public Command {
  public:
   RecordCommand()
       : Command(
             "record", "record sampling info in perf.data",
-            "Usage: simpleperf record [options] [command [command-args]]\n"
-            "    Gather sampling information when running [command].\n"
-            "    -a           System-wide collection.\n"
-            "    -b           Enable take branch stack sampling. Same as '-j any'\n"
-            "    -c count     Set event sample period.\n"
-            "    --call-graph fp | dwarf[,<dump_stack_size>]\n"
-            "                 Enable call graph recording. Use frame pointer or dwarf as the\n"
-            "                 method to parse call graph in stack. Default is dwarf,8192.\n"
-            "    --cpu cpu_item1,cpu_item2,...\n"
-            "                 Collect samples only on the selected cpus. cpu_item can be cpu\n"
-            "                 number like 1, or cpu range like 0-3.\n"
-            "    -e event1[:modifier1],event2[:modifier2],...\n"
-            "                 Select the event list to sample. Use `simpleperf list` to find\n"
-            "                 all possible event names. Modifiers can be added to define\n"
-            "                 how the event should be monitored. Possible modifiers are:\n"
-            "                   u - monitor user space events only\n"
-            "                   k - monitor kernel space events only\n"
-            "    -f freq      Set event sample frequency.\n"
-            "    -F freq      Same as '-f freq'.\n"
-            "    -g           Same as '--call-graph dwarf'.\n"
-            "    -j branch_filter1,branch_filter2,...\n"
-            "                 Enable taken branch stack sampling. Each sample\n"
-            "                 captures a series of consecutive taken branches.\n"
-            "                 The following filters are defined:\n"
-            "                   any: any type of branch\n"
-            "                   any_call: any function call or system call\n"
-            "                   any_ret: any function return or system call return\n"
-            "                   ind_call: any indirect branch\n"
-            "                   u: only when the branch target is at the user level\n"
-            "                   k: only when the branch target is in the kernel\n"
-            "                 This option requires at least one branch type among any,\n"
-            "                 any_call, any_ret, ind_call.\n"
-            "    -m mmap_pages\n"
-            "                 Set the size of the buffer used to receiving sample data from\n"
-            "                 the kernel. It should be a power of 2. The default value is 16.\n"
-            "    --no-inherit\n"
-            "                 Don't record created child threads/processes.\n"
-            "    --no-unwind  If `--call-graph dwarf` option is used, then the user's stack will\n"
-            "                 be unwound by default. Use this option to disable the unwinding of\n"
-            "                 the user's stack.\n"
-            "    -o record_file_name    Set record file name, default is perf.data.\n"
-            "    -p pid1,pid2,...\n"
-            "                 Record events on existing processes. Mutually exclusive with -a.\n"
-            "    --post-unwind\n"
-            "                 If `--call-graph dwarf` option is used, then the user's stack will\n"
-            "                 be unwound while recording by default. But it may lose records as\n"
-            "                 stacking unwinding can be time consuming. Use this option to unwind\n"
-            "                 the user's stack after recording.\n"
-            "    -t tid1,tid2,...\n"
-            "                 Record events on existing threads. Mutually exclusive with -a.\n"),
+            // clang-format off
+"Usage: simpleperf record [options] [command [command-args]]\n"
+"       Gather sampling information when running [command].\n"
+"-a     System-wide collection.\n"
+"-b     Enable take branch stack sampling. Same as '-j any'\n"
+"-c count     Set event sample period.\n"
+"--call-graph fp | dwarf[,<dump_stack_size>]\n"
+"             Enable call graph recording. Use frame pointer or dwarf debug\n"
+"             frame as the method to parse call graph in stack.\n"
+"             Default is dwarf,8192.\n"
+"--cpu cpu_item1,cpu_item2,...\n"
+"             Collect samples only on the selected cpus. cpu_item can be cpu\n"
+"             number like 1, or cpu range like 0-3.\n"
+"-e event1[:modifier1],event2[:modifier2],...\n"
+"             Select the event list to sample. Use `simpleperf list` to find\n"
+"             all possible event names. Modifiers can be added to define how\n"
+"             the event should be monitored.\n"
+"             Possible modifiers are:\n"
+"                u - monitor user space events only\n"
+"                k - monitor kernel space events only\n"
+"-f freq      Set event sample frequency.\n"
+"-F freq      Same as '-f freq'.\n"
+"-g           Same as '--call-graph dwarf'.\n"
+"-j branch_filter1,branch_filter2,...\n"
+"             Enable taken branch stack sampling. Each sample captures a series\n"
+"             of consecutive taken branches.\n"
+"             The following filters are defined:\n"
+"                any: any type of branch\n"
+"                any_call: any function call or system call\n"
+"                any_ret: any function return or system call return\n"
+"                ind_call: any indirect branch\n"
+"                u: only when the branch target is at the user level\n"
+"                k: only when the branch target is in the kernel\n"
+"             This option requires at least one branch type among any, any_call,\n"
+"             any_ret, ind_call.\n"
+"-m mmap_pages   Set the size of the buffer used to receiving sample data from\n"
+"                the kernel. It should be a power of 2. The default value is 16.\n"
+"--no-dump-kernel-symbols  Don't dump kernel symbols in perf.data. By default\n"
+"                          kernel symbols will be dumped when needed.\n"
+"--no-inherit  Don't record created child threads/processes.\n"
+"--no-unwind   If `--call-graph dwarf` option is used, then the user's stack\n"
+"              will be unwound by default. Use this option to disable the\n"
+"              unwinding of the user's stack.\n"
+"-o record_file_name    Set record file name, default is perf.data.\n"
+"-p pid1,pid2,...       Record events on existing processes. Mutually exclusive\n"
+"                       with -a.\n"
+"--post-unwind  If `--call-graph dwarf` option is used, then the user's stack\n"
+"               will be unwound while recording by default. But it may lose\n"
+"               records as stacking unwinding can be time consuming. Use this\n"
+"               option to unwind the user's stack after recording.\n"
+"-t tid1,tid2,... Record events on existing threads. Mutually exclusive with -a.\n"
+            // clang-format on
+            ),
         use_sample_freq_(true),
         sample_freq_(4000),
         system_wide_collection_(false),
@@ -122,6 +124,7 @@ class RecordCommand : public Command {
         unwind_dwarf_callchain_(true),
         post_unwind_(false),
         child_inherit_(true),
+        can_dump_kernel_symbols_(true),
         perf_mmap_pages_(16),
         record_filename_("perf.data"),
         sample_record_count_(0) {
@@ -133,14 +136,18 @@ class RecordCommand : public Command {
   bool Run(const std::vector<std::string>& args);
 
  private:
-  bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
+  bool ParseOptions(const std::vector<std::string>& args,
+                    std::vector<std::string>* non_option_args);
   bool AddMeasuredEventType(const std::string& event_type_name);
   bool SetEventSelection();
   bool CreateAndInitRecordFile();
-  std::unique_ptr<RecordFileWriter> CreateRecordFile(const std::string& filename);
+  std::unique_ptr<RecordFileWriter> CreateRecordFile(
+      const std::string& filename);
+  bool DumpKernelSymbol();
   bool DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64_t event_id);
   bool DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t event_id,
-                              bool all_threads, const std::vector<pid_t>& selected_threads);
+                              bool all_threads,
+                              const std::vector<pid_t>& selected_threads);
   bool ProcessRecord(Record* record);
   void UpdateRecordForEmbeddedElfPath(Record* record);
   void UnwindRecord(Record* record);
@@ -148,9 +155,10 @@ class RecordCommand : public Command {
   bool DumpAdditionalFeatures(const std::vector<std::string>& args);
   bool DumpBuildIdFeature();
   void CollectHitFileInfo(Record* record);
-  std::pair<std::string, uint64_t> TestForEmbeddedElf(Dso *dso, uint64_t pgoff);
+  std::pair<std::string, uint64_t> TestForEmbeddedElf(Dsodso, uint64_t pgoff);
 
-  bool use_sample_freq_;    // Use sample_freq_ when true, otherwise using sample_period_.
+  // Use sample_freq_ when true, otherwise using sample_period_.
+  bool use_sample_freq_;
   uint64_t sample_freq_;    // Sample 'sample_freq_' times per second.
   uint64_t sample_period_;  // Sample once when 'sample_period_' events occur.
 
@@ -162,6 +170,7 @@ class RecordCommand : public Command {
   bool unwind_dwarf_callchain_;
   bool post_unwind_;
   bool child_inherit_;
+  bool can_dump_kernel_symbols_;
   std::vector<pid_t> monitored_threads_;
   std::vector<int> cpus_;
   std::vector<EventTypeAndModifier> measured_event_types_;
@@ -209,19 +218,21 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
       monitored_threads_.push_back(workload->GetPid());
       event_selection_set_.SetEnableOnExec(true);
     } else {
-      LOG(ERROR) << "No threads to monitor. Try `simpleperf help record` for help\n";
+      LOG(ERROR)
+          << "No threads to monitor. Try `simpleperf help record` for help\n";
       return false;
     }
   }
 
-  // 3. Open perf_event_files, create memory mapped buffers for perf_event_files, add prepare poll
-  //    for perf_event_files.
+  // 3. Open perf_event_files, create memory mapped buffers for
+  //    perf_event_files, add prepare poll for perf_event_files.
   if (system_wide_collection_) {
     if (!event_selection_set_.OpenEventFilesForCpus(cpus_)) {
       return false;
     }
   } else {
-    if (!event_selection_set_.OpenEventFilesForThreadsOnCpus(monitored_threads_, cpus_)) {
+    if (!event_selection_set_.OpenEventFilesForThreadsOnCpus(monitored_threads_,
+                                                             cpus_)) {
       return false;
     }
   }
@@ -236,11 +247,13 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     return false;
   }
 
-  // 5. Write records in mmap buffers of perf_event_files to output file while workload is running.
+  // 5. Write records in mmap buffers of perf_event_files to output file while
+  //    workload is running.
   if (workload != nullptr && !workload->Start()) {
     return false;
   }
-  auto callback = std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1);
+  auto callback =
+      std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1);
   event_selection_set_.PrepareToReadMmapEventData(callback);
   while (true) {
     if (!event_selection_set_.ReadMmapEventData()) {
@@ -275,7 +288,7 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
                                  std::vector<std::string>* non_option_args) {
   std::set<pid_t> tid_set;
   size_t i;
-  for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
+  for (i = 0; i < args.size() && !args[i].empty() && args[i][0] == '-'; ++i) {
     if (args[i] == "-a") {
       system_wide_collection_ = true;
     } else if (args[i] == "-b") {
@@ -306,17 +319,20 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
           char* endptr;
           uint64_t size = strtoull(strs[1].c_str(), &endptr, 0);
           if (*endptr != '\0' || size > UINT_MAX) {
-            LOG(ERROR) << "invalid dump stack size in --call-graph option: " << strs[1];
+            LOG(ERROR) << "invalid dump stack size in --call-graph option: "
+                       << strs[1];
             return false;
           }
           if ((size & 7) != 0) {
-            LOG(ERROR) << "dump stack size " << size << " is not 8-byte aligned.";
+            LOG(ERROR) << "dump stack size " << size
+                       << " is not 8-byte aligned.";
             return false;
           }
           dump_stack_size_in_dwarf_sampling_ = static_cast<uint32_t>(size);
         }
       } else {
-        LOG(ERROR) << "unexpected argument for --call-graph option: " << args[i];
+        LOG(ERROR) << "unexpected argument for --call-graph option: "
+                   << args[i];
         return false;
       }
     } else if (args[i] == "--cpu") {
@@ -352,7 +368,8 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
-      std::vector<std::string> branch_sampling_types = android::base::Split(args[i], ",");
+      std::vector<std::string> branch_sampling_types =
+          android::base::Split(args[i], ",");
       for (auto& type : branch_sampling_types) {
         auto it = branch_sampling_type_map.find(type);
         if (it == branch_sampling_type_map.end()) {
@@ -372,6 +389,8 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
         return false;
       }
       perf_mmap_pages_ = pages;
+    } else if (args[i] == "--no-dump-kernel-symbols") {
+      can_dump_kernel_symbols_ = false;
     } else if (args[i] == "--no-inherit") {
       child_inherit_ = false;
     } else if (args[i] == "--no-unwind") {
@@ -405,14 +424,16 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
 
   if (!dwarf_callchain_sampling_) {
     if (!unwind_dwarf_callchain_) {
-      LOG(ERROR) << "--no-unwind is only used with `--call-graph dwarf` option.";
+      LOG(ERROR)
+          << "--no-unwind is only used with `--call-graph dwarf` option.";
       return false;
     }
     unwind_dwarf_callchain_ = false;
   }
   if (post_unwind_) {
     if (!dwarf_callchain_sampling_) {
-      LOG(ERROR) << "--post-unwind is only used with `--call-graph dwarf` option.";
+      LOG(ERROR)
+          << "--post-unwind is only used with `--call-graph dwarf` option.";
       return false;
     }
     if (!unwind_dwarf_callchain_) {
@@ -421,10 +442,11 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
     }
   }
 
-  monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(), tid_set.end());
+  monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(),
+                            tid_set.end());
   if (system_wide_collection_ && !monitored_threads_.empty()) {
-    LOG(ERROR)
-        << "Record system wide and existing processes/threads can't be used at the same time.";
+    LOG(ERROR) << "Record system wide and existing processes/threads can't be "
+                  "used at the same time.";
     return false;
   }
 
@@ -438,7 +460,8 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
 }
 
 bool RecordCommand::AddMeasuredEventType(const std::string& event_type_name) {
-  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_name);
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier =
+      ParseEventType(event_type_name);
   if (event_type_modifier == nullptr) {
     return false;
   }
@@ -464,7 +487,8 @@ bool RecordCommand::SetEventSelection() {
   if (fp_callchain_sampling_) {
     event_selection_set_.EnableFpCallChainSampling();
   } else if (dwarf_callchain_sampling_) {
-    if (!event_selection_set_.EnableDwarfCallChainSampling(dump_stack_size_in_dwarf_sampling_)) {
+    if (!event_selection_set_.EnableDwarfCallChainSampling(
+            dump_stack_size_in_dwarf_sampling_)) {
       return false;
     }
   }
@@ -478,21 +502,28 @@ bool RecordCommand::CreateAndInitRecordFile() {
     return false;
   }
   // Use first perf_event_attr and first event id to dump mmap and comm records.
-  const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
+  const perf_event_attr* attr =
+      event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
   const std::vector<std::unique_ptr<EventFd>>* fds =
       event_selection_set_.FindEventFdsByType(measured_event_types_[0]);
   uint64_t event_id = (*fds)[0]->Id();
+  if (!DumpKernelSymbol()) {
+    return false;
+  }
   if (!DumpKernelAndModuleMmaps(attr, event_id)) {
     return false;
   }
-  if (!DumpThreadCommAndMmaps(attr, event_id, system_wide_collection_, monitored_threads_)) {
+  if (!DumpThreadCommAndMmaps(attr, event_id, system_wide_collection_,
+                              monitored_threads_)) {
     return false;
   }
   return true;
 }
 
-std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(const std::string& filename) {
-  std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename);
+std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(
+    const std::string& filename) {
+  std::unique_ptr<RecordFileWriter> writer =
+      RecordFileWriter::CreateInstance(filename);
   if (writer == nullptr) {
     return nullptr;
   }
@@ -516,19 +547,49 @@ std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(const std::str
   return writer;
 }
 
-bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64_t event_id) {
+bool RecordCommand::DumpKernelSymbol() {
+  if (can_dump_kernel_symbols_) {
+    std::string kallsyms;
+    bool need_kernel_symbol = false;
+    for (const auto& type : measured_event_types_) {
+      if (!type.exclude_kernel) {
+        need_kernel_symbol = true;
+        break;
+      }
+    }
+    if (need_kernel_symbol) {
+      if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) {
+        PLOG(ERROR) << "failed to read /proc/kallsyms";
+        return false;
+      }
+    }
+    std::vector<KernelSymbolRecord> records =
+        CreateKernelSymbolRecords(std::move(kallsyms));
+    for (auto& r : records) {
+      if (!ProcessRecord(&r)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr,
+                                             uint64_t event_id) {
   KernelMmap kernel_mmap;
   std::vector<KernelMmap> module_mmaps;
   GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps);
 
-  MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
-                                            kernel_mmap.len, 0, kernel_mmap.filepath, event_id);
+  MmapRecord mmap_record =
+      CreateMmapRecord(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
+                       kernel_mmap.len, 0, kernel_mmap.filepath, event_id);
   if (!ProcessRecord(&mmap_record)) {
     return false;
   }
   for (auto& module_mmap : module_mmaps) {
-    MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, module_mmap.start_addr,
-                                              module_mmap.len, 0, module_mmap.filepath, event_id);
+    MmapRecord mmap_record =
+        CreateMmapRecord(*attr, true, UINT_MAX, 0, module_mmap.start_addr,
+                         module_mmap.len, 0, module_mmap.filepath, event_id);
     if (!ProcessRecord(&mmap_record)) {
       return false;
     }
@@ -536,9 +597,9 @@ bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64
   return true;
 }
 
-bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t event_id,
-                                           bool all_threads,
-                                           const std::vector<pid_t>& selected_threads) {
+bool RecordCommand::DumpThreadCommAndMmaps(
+    const perf_event_attr* attr, uint64_t event_id, bool all_threads,
+    const std::vector<pid_t>& selected_threads) {
   std::vector<ThreadComm> thread_comms;
   if (!GetThreadComms(&thread_comms)) {
     return false;
@@ -560,10 +621,12 @@ bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t
     if (thread.pid != thread.tid) {
       continue;
     }
-    if (!all_threads && dump_processes.find(thread.pid) == dump_processes.end()) {
+    if (!all_threads &&
+        dump_processes.find(thread.pid) == dump_processes.end()) {
       continue;
     }
-    CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm, event_id);
+    CommRecord record =
+        CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm, event_id);
     if (!ProcessRecord(&record)) {
       return false;
     }
@@ -576,9 +639,9 @@ bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t
       if (thread_mmap.executable == 0) {
         continue;  // No need to dump non-executable mmap info.
       }
-      MmapRecord record =
-          CreateMmapRecord(*attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
-                           thread_mmap.len, thread_mmap.pgoff, thread_mmap.name, event_id);
+      MmapRecord record = CreateMmapRecord(
+          *attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
+          thread_mmap.len, thread_mmap.pgoff, thread_mmap.name, event_id);
       if (!ProcessRecord(&record)) {
         return false;
       }
@@ -593,13 +656,13 @@ bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t
     if (!all_threads && dump_threads.find(thread.tid) == dump_threads.end()) {
       continue;
     }
-    ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid,
-                                              thread.pid, event_id);
+    ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid,
+                                              thread.pid, thread.pid, event_id);
     if (!ProcessRecord(&fork_record)) {
       return false;
     }
-    CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm,
-                                              event_id);
+    CommRecord comm_record =
+        CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm, event_id);
     if (!ProcessRecord(&comm_record)) {
       return false;
     }
@@ -621,11 +684,10 @@ bool RecordCommand::ProcessRecord(Record* record) {
   return result;
 }
 
-template<class RecordType>
+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) {
+  if (!r.InKernel() && 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]
@@ -636,7 +698,8 @@ void UpdateMmapRecordForEmbeddedElfPath(RecordType* record) {
     // 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);
+    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();
@@ -657,20 +720,25 @@ void RecordCommand::UpdateRecordForEmbeddedElfPath(Record* record) {
 void RecordCommand::UnwindRecord(Record* record) {
   if (record->type() == PERF_RECORD_SAMPLE) {
     SampleRecord& r = *static_cast<SampleRecord*>(record);
-    if ((r.sample_type & PERF_SAMPLE_CALLCHAIN) && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
-        (r.regs_user_data.reg_mask != 0) && (r.sample_type & PERF_SAMPLE_STACK_USER) &&
+    if ((r.sample_type & PERF_SAMPLE_CALLCHAIN) &&
+        (r.sample_type & PERF_SAMPLE_REGS_USER) &&
+        (r.regs_user_data.reg_mask != 0) &&
+        (r.sample_type & PERF_SAMPLE_STACK_USER) &&
         (!r.stack_user_data.data.empty())) {
-      ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
-      RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
+      ThreadEntry* thread =
+          thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
+      RegSet regs =
+          CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
       std::vector<char>& stack = r.stack_user_data.data;
       ArchType arch = GetArchForAbi(GetBuildArch(), r.regs_user_data.abi);
-      // Normally do strict arch check when unwinding stack. But allow unwinding 32-bit processes
-      // on 64-bit devices for system wide profiling.
+      // Normally do strict arch check when unwinding stack. But allow unwinding
+      // 32-bit processes on 64-bit devices for system wide profiling.
       bool strict_arch_check = !system_wide_collection_;
-      std::vector<uint64_t> unwind_ips = UnwindCallChain(arch, *thread, regs, stack,
-                                                         strict_arch_check);
+      std::vector<uint64_t> unwind_ips =
+          UnwindCallChain(arch, *thread, regs, stack, strict_arch_check);
       r.callchain_data.ips.push_back(PERF_CONTEXT_USER);
-      r.callchain_data.ips.insert(r.callchain_data.ips.end(), unwind_ips.begin(), unwind_ips.end());
+      r.callchain_data.ips.insert(r.callchain_data.ips.end(),
+                                  unwind_ips.begin(), unwind_ips.end());
       r.regs_user_data.abi = 0;
       r.regs_user_data.reg_mask = 0;
       r.regs_user_data.regs.clear();
@@ -683,7 +751,8 @@ void RecordCommand::UnwindRecord(Record* record) {
 
 bool RecordCommand::PostUnwind(const std::vector<std::string>& args) {
   thread_tree_.Clear();
-  std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(record_filename_);
+  std::unique_ptr<RecordFileReader> reader =
+      RecordFileReader::CreateInstance(record_filename_);
   if (reader == nullptr) {
     return false;
   }
@@ -714,13 +783,15 @@ bool RecordCommand::PostUnwind(const std::vector<std::string>& args) {
     return false;
   }
   if (rename(tmp_filename.c_str(), record_filename_.c_str()) != 0) {
-    PLOG(ERROR) << "failed to rename " << tmp_filename << " to " << record_filename_;
+    PLOG(ERROR) << "failed to rename " << tmp_filename << " to "
+                << record_filename_;
     return false;
   }
   return true;
 }
 
-bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) {
+bool RecordCommand::DumpAdditionalFeatures(
+    const std::vector<std::string>& args) {
   size_t feature_count = (branch_sampling_ != 0 ? 5 : 4);
   if (!record_file_writer_->WriteFeatureHeader(feature_count)) {
     return false;
@@ -733,10 +804,12 @@ bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args)
     PLOG(ERROR) << "uname() failed";
     return false;
   }
-  if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_OSRELEASE, uname_buf.release)) {
+  if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_OSRELEASE,
+                                               uname_buf.release)) {
     return false;
   }
-  if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_ARCH, uname_buf.machine)) {
+  if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_ARCH,
+                                               uname_buf.machine)) {
     return false;
   }
 
@@ -749,7 +822,8 @@ bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args)
   if (!record_file_writer_->WriteCmdlineFeature(cmdline)) {
     return false;
   }
-  if (branch_sampling_ != 0 && !record_file_writer_->WriteBranchStackFeature()) {
+  if (branch_sampling_ != 0 &&
+      !record_file_writer_->WriteBranchStackFeature()) {
     return false;
   }
   return true;
@@ -765,8 +839,8 @@ bool RecordCommand::DumpBuildIdFeature() {
         LOG(DEBUG) << "can't read build_id for kernel";
         continue;
       }
-      build_id_records.push_back(
-          CreateBuildIdRecord(true, UINT_MAX, build_id, DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID));
+      build_id_records.push_back(CreateBuildIdRecord(
+          true, UINT_MAX, build_id, DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID));
     } else {
       std::string path = filename;
       std::string module_name = basename(&path[0]);
@@ -777,7 +851,8 @@ bool RecordCommand::DumpBuildIdFeature() {
         LOG(DEBUG) << "can't read build_id for module " << module_name;
         continue;
       }
-      build_id_records.push_back(CreateBuildIdRecord(true, UINT_MAX, build_id, filename));
+      build_id_records.push_back(
+          CreateBuildIdRecord(true, UINT_MAX, build_id, filename));
     }
   }
   // Add build_ids for user elf files.
@@ -787,7 +862,8 @@ bool RecordCommand::DumpBuildIdFeature() {
     }
     auto tuple = SplitUrlInApk(filename);
     if (std::get<0>(tuple)) {
-      if (!GetBuildIdFromApkFile(std::get<1>(tuple), std::get<2>(tuple), &build_id)) {
+      if (!GetBuildIdFromApkFile(std::get<1>(tuple), std::get<2>(tuple),
+                                 &build_id)) {
         LOG(DEBUG) << "can't read build_id from file " << filename;
         continue;
       }
@@ -797,7 +873,8 @@ bool RecordCommand::DumpBuildIdFeature() {
         continue;
       }
     }
-    build_id_records.push_back(CreateBuildIdRecord(false, UINT_MAX, build_id, filename));
+    build_id_records.push_back(
+        CreateBuildIdRecord(false, UINT_MAX, build_id, filename));
   }
   if (!record_file_writer_->WriteBuildIdFeature(build_id_records)) {
     return false;
@@ -808,8 +885,9 @@ bool RecordCommand::DumpBuildIdFeature() {
 void RecordCommand::CollectHitFileInfo(Record* record) {
   if (record->type() == PERF_RECORD_SAMPLE) {
     auto r = *static_cast<SampleRecord*>(record);
-    bool in_kernel = ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL);
-    const ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
+    bool in_kernel = r.InKernel();
+    const ThreadEntry* thread =
+        thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
     const MapEntry* map = thread_tree_.FindMap(thread, r.ip_data.ip, in_kernel);
     if (in_kernel) {
       hit_kernel_modules_.insert(map->dso->Path());
@@ -820,5 +898,6 @@ void RecordCommand::CollectHitFileInfo(Record* record) {
 }
 
 void RegisterRecordCommand() {
-  RegisterCommand("record", [] { return std::unique_ptr<Command>(new RecordCommand()); });
+  RegisterCommand("record",
+                  [] { return std::unique_ptr<Command>(new RecordCommand()); });
 }
index 41a675b..678d985 100644 (file)
@@ -35,7 +35,8 @@ static std::unique_ptr<Command> RecordCmd() {
   return CreateCommandInstance("record");
 }
 
-static bool RunRecordCmd(std::vector<std::string> v, const char* output_file = nullptr) {
+static bool RunRecordCmd(std::vector<std::string> v,
+                         const char* output_file = nullptr) {
   std::unique_ptr<TemporaryFile> tmpfile;
   std::string out_file;
   if (output_file != nullptr) {
@@ -48,9 +49,7 @@ static bool RunRecordCmd(std::vector<std::string> v, const char* output_file = n
   return RecordCmd()->Run(v);
 }
 
-TEST(record_cmd, no_options) {
-  ASSERT_TRUE(RunRecordCmd({}));
-}
+TEST(record_cmd, no_options) { ASSERT_TRUE(RunRecordCmd({})); }
 
 TEST(record_cmd, system_wide_option) {
   TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"})));
@@ -77,14 +76,16 @@ TEST(record_cmd, output_file_option) {
 TEST(record_cmd, dump_kernel_mmap) {
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
-  std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+  std::unique_ptr<RecordFileReader> reader =
+      RecordFileReader::CreateInstance(tmpfile.path);
   ASSERT_TRUE(reader != nullptr);
   std::vector<std::unique_ptr<Record>> records = reader->DataSection();
   ASSERT_GT(records.size(), 0U);
   bool have_kernel_mmap = false;
   for (auto& record : records) {
-    if (record->header.type == PERF_RECORD_MMAP) {
-      const MmapRecord* mmap_record = static_cast<const MmapRecord*>(record.get());
+    if (record->type() == PERF_RECORD_MMAP) {
+      const MmapRecord* mmap_record =
+          static_cast<const MmapRecord*>(record.get());
       if (mmap_record->filename == DEFAULT_KERNEL_MMAP_NAME) {
         have_kernel_mmap = true;
         break;
@@ -97,10 +98,12 @@ TEST(record_cmd, dump_kernel_mmap) {
 TEST(record_cmd, dump_build_id_feature) {
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
-  std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+  std::unique_ptr<RecordFileReader> reader =
+      RecordFileReader::CreateInstance(tmpfile.path);
   ASSERT_TRUE(reader != nullptr);
   const FileHeader& file_header = reader->FileHeader();
-  ASSERT_TRUE(file_header.features[FEAT_BUILD_ID / 8] & (1 << (FEAT_BUILD_ID % 8)));
+  ASSERT_TRUE(file_header.features[FEAT_BUILD_ID / 8] &
+              (1 << (FEAT_BUILD_ID % 8)));
   ASSERT_GT(reader->FeatureSectionDescriptors().size(), 0u);
 }
 
@@ -116,8 +119,8 @@ TEST(record_cmd, branch_sampling) {
     ASSERT_TRUE(RunRecordCmd({"-j", "any,u"}));
     ASSERT_FALSE(RunRecordCmd({"-j", "u"}));
   } else {
-    GTEST_LOG_(INFO)
-        << "This test does nothing as branch stack sampling is not supported on this device.";
+    GTEST_LOG_(INFO) << "This test does nothing as branch stack sampling is "
+                        "not supported on this device.";
   }
 }
 
@@ -139,8 +142,8 @@ TEST(record_cmd, dwarf_callchain_sampling) {
     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf,16384"}));
     ASSERT_TRUE(RunRecordCmd({"-g"}));
   } else {
-    GTEST_LOG_(INFO)
-        << "This test does nothing as dwarf callchain sampling is not supported on this device.";
+    GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
+                        "not supported on this device.";
   }
 }
 
@@ -148,8 +151,8 @@ TEST(record_cmd, system_wide_dwarf_callchain_sampling) {
   if (IsDwarfCallChainSamplingSupported()) {
     TEST_IN_ROOT(RunRecordCmd({"-a", "--call-graph", "dwarf"}));
   } else {
-    GTEST_LOG_(INFO)
-        << "This test does nothing as dwarf callchain sampling is not supported on this device.";
+    GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
+                        "not supported on this device.";
   }
 }
 
@@ -157,8 +160,8 @@ TEST(record_cmd, no_unwind_option) {
   if (IsDwarfCallChainSamplingSupported()) {
     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--no-unwind"}));
   } else {
-    GTEST_LOG_(INFO)
-        << "This test does nothing as dwarf callchain sampling is not supported on this device.";
+    GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
+                        "not supported on this device.";
   }
   ASSERT_FALSE(RunRecordCmd({"--no-unwind"}));
 }
@@ -167,8 +170,8 @@ TEST(record_cmd, post_unwind_option) {
   if (IsDwarfCallChainSamplingSupported()) {
     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--post-unwind"}));
   } else {
-    GTEST_LOG_(INFO)
-        << "This test does nothing as dwarf callchain sampling is not supported on this device.";
+    GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
+                        "not supported on this device.";
   }
   ASSERT_FALSE(RunRecordCmd({"--post-unwind"}));
   ASSERT_FALSE(
@@ -178,8 +181,8 @@ TEST(record_cmd, post_unwind_option) {
 TEST(record_cmd, existing_processes) {
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(2, &workloads);
-  std::string pid_list =
-      android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
+  std::string pid_list = android::base::StringPrintf(
+      "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
   TemporaryFile tmpfile;
   ASSERT_TRUE(RecordCmd()->Run({"-p", pid_list, "-o", tmpfile.path}));
 }
@@ -188,15 +191,13 @@ TEST(record_cmd, existing_threads) {
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(2, &workloads);
   // Process id can also be used as thread id in linux.
-  std::string tid_list =
-      android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
+  std::string tid_list = android::base::StringPrintf(
+      "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
   TemporaryFile tmpfile;
   ASSERT_TRUE(RecordCmd()->Run({"-t", tid_list, "-o", tmpfile.path}));
 }
 
-TEST(record_cmd, no_monitored_threads) {
-  ASSERT_FALSE(RecordCmd()->Run({""}));
-}
+TEST(record_cmd, no_monitored_threads) { ASSERT_FALSE(RecordCmd()->Run({""})); }
 
 TEST(record_cmd, more_than_one_event_types) {
   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles,cpu-clock"}));
@@ -213,3 +214,35 @@ TEST(record_cmd, mmap_page_option) {
   ASSERT_FALSE(RunRecordCmd({"-m", "0"}));
   ASSERT_FALSE(RunRecordCmd({"-m", "7"}));
 }
+
+void CheckKernelSymbol(const std::string& path, bool need_kallsyms,
+                     bool* success) {
+  *success = false;
+  std::unique_ptr<RecordFileReader> reader =
+      RecordFileReader::CreateInstance(path);
+  ASSERT_TRUE(reader != nullptr);
+  std::vector<std::unique_ptr<Record>> records = reader->DataSection();
+  bool has_kernel_symbol_records = false;
+  for (const auto& record : records) {
+    if (record->type() == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) {
+      has_kernel_symbol_records = true;
+      auto& r = *static_cast<KernelSymbolRecord*>(record.get());
+      if (!r.end_of_symbols) {
+        ASSERT_FALSE(r.kallsyms.empty());
+      }
+    }
+  }
+  ASSERT_EQ(need_kallsyms, has_kernel_symbol_records);
+  *success = true;
+}
+
+TEST(record_cmd, kernel_symbol) {
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
+  bool success;
+  CheckKernelSymbol(tmpfile.path, true, &success);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(RunRecordCmd({"--no-dump-kernel-symbols"}, tmpfile.path));
+  CheckKernelSymbol(tmpfile.path, false, &success);
+  ASSERT_TRUE(success);
+}
index e2cd39c..2ab5827 100644 (file)
@@ -583,7 +583,7 @@ bool ReportCommand::ReadSampleTreeFromRecordFile() {
 
 bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
   BuildThreadTree(*record, &thread_tree_);
-  if (record->header.type == PERF_RECORD_SAMPLE) {
+  if (record->type() == PERF_RECORD_SAMPLE) {
     sample_tree_builder_->ProcessSampleRecord(
         *static_cast<const SampleRecord*>(record.get()));
   }
@@ -616,6 +616,7 @@ void ReportCommand::PrintReportContext(FILE* report_fp) {
   if (!record_cmdline_.empty()) {
     fprintf(report_fp, "Cmdline: %s\n", record_cmdline_.c_str());
   }
+  fprintf(report_fp, "Arch: %s\n", GetArchString(record_file_arch_).c_str());
   for (const auto& attr : event_attrs_) {
     const EventType* event_type = FindEventTypeByConfig(attr.type, attr.config);
     std::string name;
index 4c2b497..befa111 100644 (file)
@@ -274,6 +274,12 @@ TEST_F(ReportCommandTest, report_more_than_one_event_types) {
   ASSERT_NE(content.find("cpu-clock"), std::string::npos);
 }
 
+TEST_F(ReportCommandTest, report_kernel_symbol) {
+  Report(PERF_DATA_WITH_KERNEL_SYMBOL);
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("perf_event_comm_output"), std::string::npos);
+}
+
 #if defined(__linux__)
 
 static std::unique_ptr<Command> RecordCmd() {
index e34d6bc..0a29b91 100644 (file)
@@ -23,6 +23,7 @@
 #include <limits>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 
 #include "environment.h"
@@ -54,6 +55,7 @@ const char* Symbol::DemangledName() const {
 bool Dso::demangle_ = true;
 std::string Dso::symfs_dir_;
 std::string Dso::vmlinux_;
+std::string Dso::kallsyms_;
 std::unordered_map<std::string, BuildId> Dso::build_id_map_;
 size_t Dso::dso_count_;
 
@@ -142,7 +144,13 @@ Dso::Dso(DsoType type, const std::string& path)
 
 Dso::~Dso() {
   if (--dso_count_ == 0) {
+    // Clean up global variables when no longer used.
     symbol_name_allocator.Clear();
+    demangle_ = true;
+    symfs_dir_.clear();
+    vmlinux_.clear();
+    kallsyms_.clear();
+    build_id_map_.clear();
   }
 }
 
@@ -238,6 +246,22 @@ bool Dso::LoadKernel() {
   if (!vmlinux_.empty()) {
     ParseSymbolsFromElfFile(vmlinux_, build_id,
                             std::bind(VmlinuxSymbolCallback, std::placeholders::_1, this));
+  } else if (!kallsyms_.empty()) {
+    ProcessKernelSymbols(kallsyms_,
+                         std::bind(&KernelSymbolCallback, std::placeholders::_1, this));
+    bool all_zero = true;
+    for (const auto& symbol : symbols_) {
+      if (symbol.addr != 0) {
+        all_zero = false;
+        break;
+      }
+    }
+    if (all_zero) {
+      LOG(WARNING) << "Symbol addresses in /proc/kallsyms on device are all zero. "
+                      "`echo 0 >/proc/sys/kernel/kptr_restrict` or use root privilege if possible.";
+      symbols_.clear();
+      return false;
+    }
   } else {
     if (!build_id.IsEmpty()) {
       BuildId real_build_id;
@@ -250,18 +274,23 @@ bool Dso::LoadKernel() {
       }
     }
 
-    ProcessKernelSymbols("/proc/kallsyms",
+    std::string kallsyms;
+    if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) {
+      LOG(DEBUG) << "failed to read /proc/kallsyms";
+      return false;
+    }
+    ProcessKernelSymbols(kallsyms,
                          std::bind(&KernelSymbolCallback, std::placeholders::_1, this));
-    bool allZero = true;
-    for (auto& symbol : symbols_) {
+    bool all_zero = true;
+    for (const auto& symbol : symbols_) {
       if (symbol.addr != 0) {
-        allZero = false;
+        all_zero = false;
         break;
       }
     }
-    if (allZero) {
-      LOG(WARNING) << "Symbol addresses in /proc/kallsyms are all zero. Check "
-                      "/proc/sys/kernel/kptr_restrict if possible.";
+    if (all_zero) {
+      LOG(WARNING) << "Symbol addresses in /proc/kallsyms are all zero. "
+                      "`echo 0 >/proc/sys/kernel/kptr_restrict` or use root privilege if possible.";
       symbols_.clear();
       return false;
     }
index 9697319..e11ea38 100644 (file)
@@ -55,6 +55,11 @@ struct Dso {
   static std::string Demangle(const std::string& name);
   static bool SetSymFsDir(const std::string& symfs_dir);
   static void SetVmlinux(const std::string& vmlinux);
+  static void SetKallsyms(std::string kallsyms) {
+    if (!kallsyms.empty()) {
+      kallsyms_ = std::move(kallsyms);
+    }
+  }
   static void SetBuildIds(const std::vector<std::pair<std::string, BuildId>>& build_ids);
 
   static std::unique_ptr<Dso> CreateDso(DsoType dso_type, const std::string& dso_path = "");
@@ -85,6 +90,7 @@ struct Dso {
   static bool demangle_;
   static std::string symfs_dir_;
   static std::string vmlinux_;
+  static std::string kallsyms_;
   static std::unordered_map<std::string, BuildId> build_id_map_;
   static size_t dso_count_;
 
index 53119ad..578c1b1 100644 (file)
@@ -106,41 +106,6 @@ std::vector<int> GetCpusFromString(const std::string& s) {
   return std::vector<int>(cpu_set.begin(), cpu_set.end());
 }
 
-bool ProcessKernelSymbols(const std::string& symbol_file,
-                          std::function<bool(const KernelSymbol&)> callback) {
-  FILE* fp = fopen(symbol_file.c_str(), "re");
-  if (fp == nullptr) {
-    PLOG(ERROR) << "failed to open file " << symbol_file;
-    return false;
-  }
-  LineReader reader(fp);
-  char* line;
-  while ((line = reader.ReadLine()) != nullptr) {
-    // Parse line like: ffffffffa005c4e4 d __warned.41698       [libsas]
-    char name[reader.MaxLineSize()];
-    char module[reader.MaxLineSize()];
-    strcpy(module, "");
-
-    KernelSymbol symbol;
-    if (sscanf(line, "%" PRIx64 " %c %s%s", &symbol.addr, &symbol.type, name, module) < 3) {
-      continue;
-    }
-    symbol.name = name;
-    size_t module_len = strlen(module);
-    if (module_len > 2 && module[0] == '[' && module[module_len - 1] == ']') {
-      module[module_len - 1] = '\0';
-      symbol.module = &module[1];
-    } else {
-      symbol.module = nullptr;
-    }
-
-    if (callback(symbol)) {
-      return true;
-    }
-  }
-  return false;
-}
-
 static std::vector<KernelMmap> GetLoadedModules() {
   std::vector<KernelMmap> result;
   FILE* fp = fopen("/proc/modules", "re");
@@ -162,6 +127,16 @@ static std::vector<KernelMmap> GetLoadedModules() {
       result.push_back(map);
     }
   }
+  bool all_zero = true;
+  for (const auto& map : result) {
+    if (map.start_addr != 0) {
+      all_zero = false;
+    }
+  }
+  if (all_zero) {
+    LOG(DEBUG) << "addresses in /proc/modules are all zero, so ignore kernel modules";
+    return std::vector<KernelMmap>();
+  }
   return result;
 }
 
index c405af8..cb44ebe 100644 (file)
@@ -69,15 +69,4 @@ bool GetValidThreadsFromThreadString(const std::string& tid_str, std::set<pid_t>
 
 bool GetExecPath(std::string* exec_path);
 
-// Expose the following functions for unit tests.
-struct KernelSymbol {
-  uint64_t addr;
-  char type;
-  const char* name;
-  const char* module;  // If nullptr, the symbol is not in a kernel module.
-};
-
-bool ProcessKernelSymbols(const std::string& symbol_file,
-                          std::function<bool(const KernelSymbol&)> callback);
-
 #endif  // SIMPLE_PERF_ENVIRONMENT_H_
index 6bca7b8..9b4cbab 100644 (file)
 
 #include <gtest/gtest.h>
 
-#include <functional>
-#include <android-base/file.h>
-#include <android-base/test_utils.h>
-
 #include "environment.h"
 
 TEST(environment, GetCpusFromString) {
@@ -28,47 +24,3 @@ TEST(environment, GetCpusFromString) {
   ASSERT_EQ(GetCpusFromString("0,2-3"), std::vector<int>({0, 2, 3}));
   ASSERT_EQ(GetCpusFromString("1,0-3,3,4"), std::vector<int>({0, 1, 2, 3, 4}));
 }
-
-static bool ModulesMatch(const char* p, const char* q) {
-  if (p == nullptr && q == nullptr) {
-    return true;
-  }
-  if (p != nullptr && q != nullptr) {
-    return strcmp(p, q) == 0;
-  }
-  return false;
-}
-
-static bool KernelSymbolsMatch(const KernelSymbol& sym1, const KernelSymbol& sym2) {
-  return sym1.addr == sym2.addr &&
-         sym1.type == sym2.type &&
-         strcmp(sym1.name, sym2.name) == 0 &&
-         ModulesMatch(sym1.module, sym2.module);
-}
-
-TEST(environment, ProcessKernelSymbols) {
-  std::string data =
-      "ffffffffa005c4e4 d __warned.41698   [libsas]\n"
-      "aaaaaaaaaaaaaaaa T _text\n"
-      "cccccccccccccccc c ccccc\n";
-  TemporaryFile tempfile;
-  ASSERT_TRUE(android::base::WriteStringToFile(data, tempfile.path));
-  KernelSymbol expected_symbol;
-  expected_symbol.addr = 0xffffffffa005c4e4ULL;
-  expected_symbol.type = 'd';
-  expected_symbol.name = "__warned.41698";
-  expected_symbol.module = "libsas";
-  ASSERT_TRUE(ProcessKernelSymbols(
-      tempfile.path, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
-
-  expected_symbol.addr = 0xaaaaaaaaaaaaaaaaULL;
-  expected_symbol.type = 'T';
-  expected_symbol.name = "_text";
-  expected_symbol.module = nullptr;
-  ASSERT_TRUE(ProcessKernelSymbols(
-      tempfile.path, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
-
-  expected_symbol.name = "non_existent_symbol";
-  ASSERT_FALSE(ProcessKernelSymbols(
-      tempfile.path, std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
-}
index 4c21d39..fa0e113 100644 (file)
@@ -68,4 +68,7 @@ static BuildId native_lib_build_id("8ed5755a7fdc07586ca228b8ee21621bce2c7a97");
 // perf_with_two_event_types.data is generated by sampling using -e cpu-cycles,cpu-clock option.
 static const std::string PERF_DATA_WITH_TWO_EVENT_TYPES = "perf_with_two_event_types.data";
 
+// perf_with_kernel_symbol.data is generated by `sudo simpleperf record ls -l`.
+static const std::string PERF_DATA_WITH_KERNEL_SYMBOL = "perf_with_kernel_symbol.data";
+
 #endif  // SIMPLE_PERF_GET_TEST_DATA_H_
index 37833ed..24af5ce 100644 (file)
@@ -25,10 +25,6 @@ std::vector<uint64_t> UnwindCallChain(ArchType, const ThreadEntry&, const RegSet
   return std::vector<uint64_t>();
 }
 
-bool ProcessKernelSymbols(const std::string&, std::function<bool(const KernelSymbol&)>) {
-  return false;
-}
-
 bool GetKernelBuildId(BuildId*) {
   return false;
 }
index 0216275..701b295 100644 (file)
@@ -34,7 +34,7 @@ static std::string RecordTypeToString(int record_type) {
       {PERF_RECORD_THROTTLE, "throttle"}, {PERF_RECORD_UNTHROTTLE, "unthrottle"},
       {PERF_RECORD_FORK, "fork"},         {PERF_RECORD_READ, "read"},
       {PERF_RECORD_SAMPLE, "sample"},     {PERF_RECORD_BUILD_ID, "build_id"},
-      {PERF_RECORD_MMAP2, "mmap2"},
+      {PERF_RECORD_MMAP2, "mmap2"},       {SIMPLE_PERF_RECORD_KERNEL_SYMBOL, "kernel_symbol"},
   };
 
   auto it = record_type_names.find(record_type);
@@ -96,7 +96,9 @@ void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p,
     if (sample_type & PERF_SAMPLE_CPU) {
       MoveFromBinaryFormat(cpu_data, p);
     }
-    // TODO: Add parsing of PERF_SAMPLE_IDENTIFIER.
+    if (sample_type & PERF_SAMPLE_IDENTIFIER) {
+      MoveFromBinaryFormat(id_data, p);
+    }
   }
   CHECK_LE(p, end);
   if (p < end) {
@@ -132,7 +134,7 @@ void SampleId::Dump(size_t indent) const {
     if (sample_type & PERF_SAMPLE_TIME) {
       PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time);
     }
-    if (sample_type & PERF_SAMPLE_ID) {
+    if (sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER)) {
       PrintIndented(indent, "sample_id: id %" PRId64 "\n", id_data.id);
     }
     if (sample_type & PERF_SAMPLE_STREAM_ID) {
@@ -162,21 +164,16 @@ size_t SampleId::Size() const {
     if (sample_type & PERF_SAMPLE_CPU) {
       size += sizeof(PerfSampleCpuType);
     }
+    if (sample_type & PERF_SAMPLE_IDENTIFIER) {
+      size += sizeof(PerfSampleIdType);
+    }
   }
   return size;
 }
 
-Record::Record() {
-  memset(&header, 0, sizeof(header));
-}
-
-Record::Record(const perf_event_header* pheader) {
-  header = *pheader;
-}
-
 void Record::Dump(size_t indent) const {
   PrintIndented(indent, "record %s: type %u, misc %u, size %u\n",
-                RecordTypeToString(header.type).c_str(), header.type, header.misc, header.size);
+                RecordTypeToString(type()).c_str(), type(), misc(), size());
   DumpData(indent + 1);
   sample_id.Dump(indent + 1);
 }
@@ -188,7 +185,7 @@ uint64_t Record::Timestamp() const {
 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
-  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
   MoveFromBinaryFormat(data, p);
   filename = p;
   p += ALIGN(filename.size() + 1, 8);
@@ -197,7 +194,7 @@ MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* phe
 }
 
 std::vector<char> MmapRecord::BinaryFormat() const {
-  std::vector<char> buf(header.size);
+  std::vector<char> buf(size());
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
   MoveToBinaryFormat(data, p);
@@ -208,7 +205,7 @@ std::vector<char> MmapRecord::BinaryFormat() const {
 }
 
 void MmapRecord::AdjustSizeBasedOnData() {
-  header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
+  SetSize(header_size() + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size());
 }
 
 void MmapRecord::DumpData(size_t indent) const {
@@ -220,7 +217,7 @@ void MmapRecord::DumpData(size_t indent) const {
 Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
-  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
   MoveFromBinaryFormat(data, p);
   filename = p;
   p += ALIGN(filename.size() + 1, 8);
@@ -229,7 +226,7 @@ Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* p
 }
 
 std::vector<char> Mmap2Record::BinaryFormat() const {
-  std::vector<char> buf(header.size);
+  std::vector<char> buf(size());
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
   MoveToBinaryFormat(data, p);
@@ -240,7 +237,7 @@ std::vector<char> Mmap2Record::BinaryFormat() const {
 }
 
 void Mmap2Record::AdjustSizeBasedOnData() {
-  header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
+  SetSize(header_size() + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size());
 }
 
 void Mmap2Record::DumpData(size_t indent) const {
@@ -256,7 +253,7 @@ void Mmap2Record::DumpData(size_t indent) const {
 CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
-  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
   MoveFromBinaryFormat(data, p);
   comm = p;
   p += ALIGN(strlen(p) + 1, 8);
@@ -265,7 +262,7 @@ CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* phe
 }
 
 std::vector<char> CommRecord::BinaryFormat() const {
-  std::vector<char> buf(header.size);
+  std::vector<char> buf(size());
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
   MoveToBinaryFormat(data, p);
@@ -282,14 +279,14 @@ void CommRecord::DumpData(size_t indent) const {
 ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
-  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
   MoveFromBinaryFormat(data, p);
   CHECK_LE(p, end);
   sample_id.ReadFromBinaryFormat(attr, p, end);
 }
 
 std::vector<char> ExitOrForkRecord::BinaryFormat() const {
-  std::vector<char> buf(header.size);
+  std::vector<char> buf(size());
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
   MoveToBinaryFormat(data, p);
@@ -305,9 +302,12 @@ void ExitOrForkRecord::DumpData(size_t indent) const {
 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader)
     : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
-  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
   sample_type = attr.sample_type;
 
+  if (sample_type & PERF_SAMPLE_IDENTIFIER) {
+    MoveFromBinaryFormat(id_data, p);
+  }
   if (sample_type & PERF_SAMPLE_IP) {
     MoveFromBinaryFormat(ip_data, p);
   }
@@ -385,9 +385,12 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header*
 }
 
 std::vector<char> SampleRecord::BinaryFormat() const {
-  std::vector<char> buf(header.size);
+  std::vector<char> buf(size());
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
+  if (sample_type & PERF_SAMPLE_IDENTIFIER) {
+    MoveToBinaryFormat(id_data, p);
+  }
   if (sample_type & PERF_SAMPLE_IP) {
     MoveToBinaryFormat(ip_data, p);
   }
@@ -450,9 +453,9 @@ std::vector<char> SampleRecord::BinaryFormat() const {
 
 void SampleRecord::AdjustSizeBasedOnData() {
   size_t size = BinaryFormat().size();
-  LOG(DEBUG) << "Record (type " << RecordTypeToString(header.type) << ") size is changed from "
-      << header.size << " to " << size;
-  header.size = size;
+  LOG(DEBUG) << "Record (type " << RecordTypeToString(type())
+      << ") size is changed from " << this->size() << " to " << size;
+  SetSize(size);
 }
 
 void SampleRecord::DumpData(size_t indent) const {
@@ -469,7 +472,7 @@ void SampleRecord::DumpData(size_t indent) const {
   if (sample_type & PERF_SAMPLE_ADDR) {
     PrintIndented(indent, "addr %p\n", reinterpret_cast<void*>(addr_data.addr));
   }
-  if (sample_type & PERF_SAMPLE_ID) {
+  if (sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER)) {
     PrintIndented(indent, "id %" PRId64 "\n", id_data.id);
   }
   if (sample_type & PERF_SAMPLE_STREAM_ID) {
@@ -534,7 +537,7 @@ uint64_t SampleRecord::Timestamp() const {
 
 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
-  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
   MoveFromBinaryFormat(pid, p);
   build_id = BuildId(p, BUILD_ID_SIZE);
   p += ALIGN(build_id.Size(), 8);
@@ -544,7 +547,7 @@ BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader)
 }
 
 std::vector<char> BuildIdRecord::BinaryFormat() const {
-  std::vector<char> buf(header.size);
+  std::vector<char> buf(size());
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
   MoveToBinaryFormat(pid, p);
@@ -560,14 +563,47 @@ void BuildIdRecord::DumpData(size_t indent) const {
   PrintIndented(indent, "filename %s\n", filename.c_str());
 }
 
+KernelSymbolRecord::KernelSymbolRecord(const perf_event_header* pheader) : Record(pheader) {
+  const char* p = reinterpret_cast<const char*>(pheader + 1);
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
+  uint32_t end_flag;
+  MoveFromBinaryFormat(end_flag, p);
+  end_of_symbols = (end_flag == 1);
+  uint32_t size;
+  MoveFromBinaryFormat(size, p);
+  kallsyms.resize(size);
+  if (size != 0u) {
+    memcpy(&kallsyms[0], p, size);
+  }
+  p += ALIGN(size, 8);
+  CHECK_EQ(p, end);
+}
+
+std::vector<char> KernelSymbolRecord::BinaryFormat() const {
+  std::vector<char> buf(size());
+  char* p = buf.data();
+  MoveToBinaryFormat(header, p);
+  uint32_t end_flag = (end_of_symbols ? 1 : 0);
+  MoveToBinaryFormat(end_flag, p);
+  uint32_t size = static_cast<uint32_t>(kallsyms.size());
+  MoveToBinaryFormat(size, p);
+  memcpy(p, kallsyms.data(), size);
+  return buf;
+}
+
+void KernelSymbolRecord::DumpData(size_t indent) const {
+  PrintIndented(indent, "end_of_symbols: %s\n", end_of_symbols ? "true" : "false");
+  PrintIndented(indent, "kallsyms: %s\n", kallsyms.c_str());
+}
+
 UnknownRecord::UnknownRecord(const perf_event_header* pheader) : Record(pheader) {
   const char* p = reinterpret_cast<const char*>(pheader + 1);
-  const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+  const char* end = reinterpret_cast<const char*>(pheader) + size();
   data.insert(data.end(), p, end);
 }
 
 std::vector<char> UnknownRecord::BinaryFormat() const {
-  std::vector<char> buf(header.size);
+  std::vector<char> buf(size());
   char* p = buf.data();
   MoveToBinaryFormat(header, p);
   MoveToBinaryFormat(data.data(), data.size(), p);
@@ -592,6 +628,8 @@ std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
       return std::unique_ptr<Record>(new ForkRecord(attr, pheader));
     case PERF_RECORD_SAMPLE:
       return std::unique_ptr<Record>(new SampleRecord(attr, pheader));
+    case SIMPLE_PERF_RECORD_KERNEL_SYMBOL:
+      return std::unique_ptr<Record>(new KernelSymbolRecord(pheader));
     default:
       return std::unique_ptr<Record>(new UnknownRecord(pheader));
   }
@@ -604,10 +642,11 @@ std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr
   const char* end = buf + buf_size;
   while (p < end) {
     const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
-    CHECK_LE(p + header->size, end);
-    CHECK_NE(0u, header->size);
+    uint32_t size = header->size;
+    CHECK_LE(p + size, end);
+    CHECK_NE(0u, size);
     result.push_back(ReadRecordFromBuffer(attr, header));
-    p += header->size;
+    p += size;
   }
   return result;
 }
@@ -616,8 +655,7 @@ MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_
                             uint64_t addr, uint64_t len, uint64_t pgoff,
                             const std::string& filename, uint64_t event_id) {
   MmapRecord record;
-  record.header.type = PERF_RECORD_MMAP;
-  record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
+  record.SetTypeAndMisc(PERF_RECORD_MMAP, in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
   record.data.pid = pid;
   record.data.tid = tid;
   record.data.addr = addr;
@@ -625,56 +663,72 @@ MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_
   record.data.pgoff = pgoff;
   record.filename = filename;
   size_t sample_id_size = record.sample_id.CreateContent(attr, event_id);
-  record.header.size = sizeof(record.header) + sizeof(record.data) +
-                       ALIGN(record.filename.size() + 1, 8) + sample_id_size;
+  record.SetSize(record.header_size() + sizeof(record.data) +
+                 ALIGN(record.filename.size() + 1, 8) + sample_id_size);
   return record;
 }
 
 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
                             const std::string& comm, uint64_t event_id) {
   CommRecord record;
-  record.header.type = PERF_RECORD_COMM;
-  record.header.misc = 0;
+  record.SetTypeAndMisc(PERF_RECORD_COMM, 0);
   record.data.pid = pid;
   record.data.tid = tid;
   record.comm = comm;
   size_t sample_id_size = record.sample_id.CreateContent(attr, event_id);
-  record.header.size = sizeof(record.header) + sizeof(record.data) +
-                       ALIGN(record.comm.size() + 1, 8) + sample_id_size;
+  record.SetSize(record.header_size() + sizeof(record.data) +
+                 ALIGN(record.comm.size() + 1, 8) + sample_id_size);
   return record;
 }
 
 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
                             uint32_t ptid, uint64_t event_id) {
   ForkRecord record;
-  record.header.type = PERF_RECORD_FORK;
-  record.header.misc = 0;
+  record.SetTypeAndMisc(PERF_RECORD_FORK, 0);
   record.data.pid = pid;
   record.data.ppid = ppid;
   record.data.tid = tid;
   record.data.ptid = ptid;
   record.data.time = 0;
   size_t sample_id_size = record.sample_id.CreateContent(attr, event_id);
-  record.header.size = sizeof(record.header) + sizeof(record.data) + sample_id_size;
+  record.SetSize(record.header_size() + sizeof(record.data) + sample_id_size);
   return record;
 }
 
 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
                                   const std::string& filename) {
   BuildIdRecord record;
-  record.header.type = PERF_RECORD_BUILD_ID;
-  record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
+  record.SetTypeAndMisc(PERF_RECORD_BUILD_ID, in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
   record.pid = pid;
   record.build_id = build_id;
   record.filename = filename;
-  record.header.size = sizeof(record.header) + sizeof(record.pid) +
-                       ALIGN(record.build_id.Size(), 8) + ALIGN(filename.size() + 1, 64);
+  record.SetSize(record.header_size() + sizeof(record.pid) +
+                       ALIGN(record.build_id.Size(), 8) + ALIGN(filename.size() + 1, 64));
   return record;
 }
 
+std::vector<KernelSymbolRecord> CreateKernelSymbolRecords(const std::string& kallsyms) {
+  std::vector<KernelSymbolRecord> result;
+  size_t left_bytes = kallsyms.size();
+  const size_t max_bytes_per_record = 64000;
+  const char* p = kallsyms.c_str();
+  while (left_bytes > 0) {
+    size_t used_bytes = std::min(left_bytes, max_bytes_per_record);
+    KernelSymbolRecord r;
+    r.SetTypeAndMisc(SIMPLE_PERF_RECORD_KERNEL_SYMBOL, 0);
+    r.end_of_symbols = (used_bytes == left_bytes);
+    r.kallsyms.assign(p, used_bytes);
+    r.SetSize(r.header_size() + 8 + ALIGN(r.kallsyms.size(), 8));
+    result.push_back(r);
+    p += used_bytes;
+    left_bytes -= used_bytes;
+  }
+  return result;
+}
+
 bool RecordCache::RecordWithSeq::IsHappensBefore(const RecordWithSeq& other) const {
-  bool is_sample = (record->header.type == PERF_RECORD_SAMPLE);
-  bool is_other_sample = (other.record->header.type == PERF_RECORD_SAMPLE);
+  bool is_sample = (record->type() == PERF_RECORD_SAMPLE);
+  bool is_other_sample = (other.record->type() == PERF_RECORD_SAMPLE);
   uint64_t time = record->Timestamp();
   uint64_t other_time = other.record->Timestamp();
   // The record with smaller time happens first.
index 85dcbc7..55512b8 100644 (file)
@@ -25,6 +25,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/logging.h>
+
 #include "build_id.h"
 #include "perf_event.h"
 
@@ -34,11 +36,15 @@ struct ThreadComm;
 struct ThreadMmap;
 
 enum user_record_type {
+  PERF_RECORD_USER_DEFINED_TYPE_START = 64,
   PERF_RECORD_ATTR = 64,
   PERF_RECORD_EVENT_TYPE,
   PERF_RECORD_TRACING_DATA,
   PERF_RECORD_BUILD_ID,
   PERF_RECORD_FINISHED_ROUND,
+
+  SIMPLE_PERF_RECORD_TYPE_START = 32768,
+  SIMPLE_PERF_RECORD_KERNEL_SYMBOL,
 };
 
 struct PerfSampleIpType {
@@ -139,24 +145,46 @@ struct Record {
   perf_event_header header;
   SampleId sample_id;
 
-  Record();
-  Record(const perf_event_header* pheader);
+  Record() {
+    memset(&header, 0, sizeof(header));
+  }
+  Record(const perf_event_header* pheader) {
+    header = *pheader;
+  }
 
   virtual ~Record() {
   }
 
+  uint32_t type() const {
+    return header.type;
+  }
+
+  uint16_t misc() const {
+    return header.misc;
+  }
+
   size_t size() const {
     return header.size;
   }
 
-  uint32_t type() const {
-    return header.type;
+  static uint32_t header_size() {
+    return sizeof(perf_event_header);
   }
 
   bool InKernel() const {
     return (header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL;
   }
 
+  void SetTypeAndMisc(uint32_t type, uint16_t misc) {
+    header.type = type;
+    header.misc = misc;
+  }
+
+  void SetSize(uint32_t size) {
+    CHECK_LT(size, 1u << 16);
+    header.size = size;
+  }
+
   void Dump(size_t indent = 0) const;
   virtual std::vector<char> BinaryFormat() const = 0;
   virtual uint64_t Timestamp() const;
@@ -299,6 +327,20 @@ struct BuildIdRecord : public Record {
   void DumpData(size_t indent) const override;
 };
 
+struct KernelSymbolRecord : public Record {
+  bool end_of_symbols;
+  std::string kallsyms;
+
+  KernelSymbolRecord() {
+  }
+
+  KernelSymbolRecord(const perf_event_header* pheader);
+  std::vector<char> BinaryFormat() const override;
+
+ protected:
+  void DumpData(size_t indent) const override;
+};
+
 // UnknownRecord is used for unknown record types, it makes sure all unknown records
 // are not changed when modifying perf.data.
 struct UnknownRecord : public Record {
@@ -371,5 +413,6 @@ ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t
                             uint32_t ptid, uint64_t event_id);
 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
                                   const std::string& filename);
+std::vector<KernelSymbolRecord> CreateKernelSymbolRecords(const std::string& kallsyms);
 
 #endif  // SIMPLE_PERF_RECORD_H_
index 03768dc..a15fad2 100644 (file)
@@ -31,13 +31,15 @@ static void CheckBuildIdRecordDataEqual(const BuildIdRecord& r1, const BuildIdRe
 }
 
 static void CheckRecordEqual(const Record& r1, const Record& r2) {
-  ASSERT_EQ(0, memcmp(&r1.header, &r2.header, sizeof(r1.header)));
+  ASSERT_EQ(r1.type(), r2.type());
+  ASSERT_EQ(r1.misc(), r2.misc());
+  ASSERT_EQ(r1.size(), r2.size());
   ASSERT_EQ(0, memcmp(&r1.sample_id, &r2.sample_id, sizeof(r1.sample_id)));
-  if (r1.header.type == PERF_RECORD_MMAP) {
+  if (r1.type() == PERF_RECORD_MMAP) {
     CheckMmapRecordDataEqual(static_cast<const MmapRecord&>(r1), static_cast<const MmapRecord&>(r2));
-  } else if (r1.header.type == PERF_RECORD_COMM) {
+  } else if (r1.type() == PERF_RECORD_COMM) {
     CheckCommRecordDataEqual(static_cast<const CommRecord&>(r1), static_cast<const CommRecord&>(r2));
-  } else if (r1.header.type == PERF_RECORD_BUILD_ID) {
+  } else if (r1.type() == PERF_RECORD_BUILD_ID) {
     CheckBuildIdRecordDataEqual(static_cast<const BuildIdRecord&>(r1),
                                 static_cast<const BuildIdRecord&>(r2));
   }
index 1165494..9b97936 100644 (file)
@@ -213,29 +213,40 @@ std::unique_ptr<Record> RecordFileReader::ReadRecord() {
     PLOG(ERROR) << "failed to read file " << filename_;
     return nullptr;
   }
-  perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]);
-  if (buf.size() < header->size) {
-    buf.resize(header->size);
-    header = reinterpret_cast<perf_event_header*>(&buf[0]);
-  }
-  if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, record_fp_) != 1) {
-    PLOG(ERROR) << "failed to read file " << filename_;
-    return nullptr;
+  perf_event_header* pheader = reinterpret_cast<perf_event_header*>(&buf[0]);
+  if (buf.size() < pheader->size) {
+    buf.resize(pheader->size);
+    pheader = reinterpret_cast<perf_event_header*>(&buf[0]);
+  }
+  if (buf.size() > sizeof(perf_event_header)) {
+    if (fread(&buf[sizeof(perf_event_header)], pheader->size - sizeof(perf_event_header), 1, record_fp_) != 1) {
+      PLOG(ERROR) << "failed to read file " << filename_;
+      return nullptr;
+    }
   }
   const perf_event_attr* attr = &file_attrs_[0].attr;
-  if (file_attrs_.size() > 1) {
+  if (file_attrs_.size() > 1 && pheader->type < PERF_RECORD_USER_DEFINED_TYPE_START) {
+    bool has_event_id = false;
     uint64_t event_id;
-    if (header->type == PERF_RECORD_SAMPLE) {
-      event_id = *reinterpret_cast<uint64_t*>(&buf[event_id_pos_in_sample_records_]);
+    if (pheader->type == PERF_RECORD_SAMPLE) {
+      if (pheader->size > event_id_pos_in_sample_records_ + sizeof(uint64_t)) {
+        has_event_id = true;
+        event_id = *reinterpret_cast<uint64_t*>(&buf[event_id_pos_in_sample_records_]);
+      }
     } else {
-      event_id = *reinterpret_cast<uint64_t*>(
-          &buf[header->size - event_id_reverse_pos_in_non_sample_records_]);
+      if (pheader->size > event_id_reverse_pos_in_non_sample_records_) {
+        has_event_id = true;
+        event_id = *reinterpret_cast<uint64_t*>(&buf[pheader->size - event_id_reverse_pos_in_non_sample_records_]);
+      }
+    }
+    if (has_event_id) {
+      auto it = event_id_to_attr_map_.find(event_id);
+      if (it != event_id_to_attr_map_.end()) {
+        attr = it->second;
+      }
     }
-    auto it = event_id_to_attr_map_.find(event_id);
-    CHECK(it != event_id_to_attr_map_.end());
-    attr = it->second;
   }
-  return ReadRecordFromBuffer(*attr, header);
+  return ReadRecordFromBuffer(*attr, pheader);
 }
 
 bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) {
@@ -246,6 +257,9 @@ bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data)
   }
   SectionDesc section = it->second;
   data->resize(section.size);
+  if (section.size == 0) {
+    return true;
+  }
   if (fseek(record_fp_, section.offset, SEEK_SET) != 0) {
     PLOG(ERROR) << "failed to fseek()";
     return false;
@@ -291,9 +305,9 @@ std::vector<BuildIdRecord> RecordFileReader::ReadBuildIdFeature() {
     CHECK_LE(p + header->size, end);
     BuildIdRecord record(header);
     // Set type explicitly as the perf.data produced by perf doesn't set it.
-    record.header.type = PERF_RECORD_BUILD_ID;
+    record.SetTypeAndMisc(PERF_RECORD_BUILD_ID, record.misc());
     result.push_back(record);
-    p += header->size;
+    p += record.size();
   }
   return result;
 }
diff --git a/simpleperf/testdata/perf_with_kernel_symbol.data b/simpleperf/testdata/perf_with_kernel_symbol.data
new file mode 100644 (file)
index 0000000..30b14c9
Binary files /dev/null and b/simpleperf/testdata/perf_with_kernel_symbol.data differ
index d971404..70ed739 100644 (file)
@@ -87,7 +87,7 @@ void ThreadTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff,
     return;
   }
   Dso* dso = FindKernelDsoOrNew(filename);
-  MapEntry* map = AllocateMap(MapEntry(start_addr, len, pgoff, time, dso));
+  MapEntry* map = AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, true));
   FixOverlappedMap(&kernel_map_tree_, map);
   auto pair = kernel_map_tree_.insert(map);
   CHECK(pair.second);
@@ -95,9 +95,6 @@ void ThreadTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff,
 
 Dso* ThreadTree::FindKernelDsoOrNew(const std::string& filename) {
   if (filename == DEFAULT_KERNEL_MMAP_NAME) {
-    if (kernel_dso_ == nullptr) {
-      kernel_dso_ = Dso::CreateDso(DSO_KERNEL);
-    }
     return kernel_dso_.get();
   }
   auto it = module_dso_tree_.find(filename);
@@ -112,7 +109,7 @@ void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t le
                               uint64_t time, const std::string& filename) {
   ThreadEntry* thread = FindThreadOrNew(pid, tid);
   Dso* dso = FindUserDsoOrNew(filename);
-  MapEntry* map = AllocateMap(MapEntry(start_addr, len, pgoff, time, dso));
+  MapEntry* map = AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, false));
   FixOverlappedMap(&thread->maps, map);
   auto pair = thread->maps.insert(map);
   CHECK(pair.second);
@@ -145,13 +142,13 @@ void ThreadTree::FixOverlappedMap(std::set<MapEntry*, MapComparator>* map_set, c
       MapEntry* old = *it;
       if (old->start_addr < map->start_addr) {
         MapEntry* before = AllocateMap(MapEntry(old->start_addr, map->start_addr - old->start_addr,
-                                                old->pgoff, old->time, old->dso));
+                                                old->pgoff, old->time, old->dso, old->in_kernel));
         map_set->insert(before);
       }
       if (old->get_end_addr() > map->get_end_addr()) {
         MapEntry* after = AllocateMap(
             MapEntry(map->get_end_addr(), old->get_end_addr() - map->get_end_addr(),
-                     map->get_end_addr() - old->start_addr + old->pgoff, old->time, old->dso));
+                     map->get_end_addr() - old->start_addr + old->pgoff, old->time, old->dso, old->in_kernel));
         map_set->insert(after);
       }
 
@@ -167,7 +164,7 @@ static bool IsAddrInMap(uint64_t addr, const MapEntry* map) {
 static MapEntry* FindMapByAddr(const std::set<MapEntry*, MapComparator>& maps, uint64_t addr) {
   // Construct a map_entry which is strictly after the searched map_entry, based on MapComparator.
   MapEntry find_map(addr, std::numeric_limits<uint64_t>::max(), 0,
-                    std::numeric_limits<uint64_t>::max(), nullptr);
+                    std::numeric_limits<uint64_t>::max(), nullptr, false);
   auto it = maps.upper_bound(&find_map);
   if (it != maps.begin() && IsAddrInMap(addr, *--it)) {
     return *it;
@@ -202,6 +199,13 @@ const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip) {
     vaddr_in_file = ip - map->start_addr + map->dso->MinVirtualAddress();
   }
   const Symbol* symbol = map->dso->FindSymbol(vaddr_in_file);
+  if (symbol == nullptr && map->in_kernel && map->dso != kernel_dso_.get()) {
+    // It is in a kernel module, but we can't find the kernel module file, or
+    // the kernel module file contains no symbol. Try finding the symbol in
+    // /proc/kallsyms.
+    vaddr_in_file = ip;
+    symbol = kernel_dso_->FindSymbol(vaddr_in_file);
+  }
   if (symbol == nullptr) {
     symbol = &unknown_symbol_;
   }
@@ -221,18 +225,18 @@ void ThreadTree::Clear() {
 }  // namespace simpleperf
 
 void BuildThreadTree(const Record& record, ThreadTree* thread_tree) {
-  if (record.header.type == PERF_RECORD_MMAP) {
+  if (record.type() == PERF_RECORD_MMAP) {
     const MmapRecord& r = *static_cast<const MmapRecord*>(&record);
-    if ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL) {
+    if (r.InKernel()) {
       thread_tree->AddKernelMap(r.data.addr, r.data.len, r.data.pgoff, r.sample_id.time_data.time,
                                 r.filename);
     } else {
       thread_tree->AddThreadMap(r.data.pid, r.data.tid, r.data.addr, r.data.len, r.data.pgoff,
                                 r.sample_id.time_data.time, r.filename);
     }
-  } else if (record.header.type == PERF_RECORD_MMAP2) {
+  } else if (record.type() == PERF_RECORD_MMAP2) {
     const Mmap2Record& r = *static_cast<const Mmap2Record*>(&record);
-    if ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL) {
+    if (r.InKernel()) {
       thread_tree->AddKernelMap(r.data.addr, r.data.len, r.data.pgoff, r.sample_id.time_data.time,
                                 r.filename);
     } else {
@@ -241,11 +245,19 @@ void BuildThreadTree(const Record& record, ThreadTree* thread_tree) {
       thread_tree->AddThreadMap(r.data.pid, r.data.tid, r.data.addr, r.data.len, r.data.pgoff,
                                 r.sample_id.time_data.time, filename);
     }
-  } else if (record.header.type == PERF_RECORD_COMM) {
+  } else if (record.type() == PERF_RECORD_COMM) {
     const CommRecord& r = *static_cast<const CommRecord*>(&record);
     thread_tree->AddThread(r.data.pid, r.data.tid, r.comm);
-  } else if (record.header.type == PERF_RECORD_FORK) {
+  } else if (record.type() == PERF_RECORD_FORK) {
     const ForkRecord& r = *static_cast<const ForkRecord*>(&record);
     thread_tree->ForkThread(r.data.pid, r.data.tid, r.data.ppid, r.data.ptid);
+  } else if (record.type() == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) {
+    const auto& r = *static_cast<const KernelSymbolRecord*>(&record);
+    static std::string kallsyms;
+    kallsyms += r.kallsyms;
+    if (r.end_of_symbols) {
+      Dso::SetKallsyms(std::move(kallsyms));
+      kallsyms.clear();
+    }
   }
 }
index 6dc7dd7..5d7d0c1 100644 (file)
@@ -33,9 +33,10 @@ struct MapEntry {
   uint64_t pgoff;
   uint64_t time;  // Map creation time.
   Dso* dso;
+  bool in_kernel;
 
-  MapEntry(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, Dso* dso)
-      : start_addr(start_addr), len(len), pgoff(pgoff), time(time), dso(dso) {
+  MapEntry(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, Dso* dso, bool in_kernel)
+      : start_addr(start_addr), len(len), pgoff(pgoff), time(time), dso(dso), in_kernel(in_kernel) {
   }
   MapEntry() {
   }
@@ -61,7 +62,8 @@ class ThreadTree {
   ThreadTree() : unknown_symbol_("unknown", 0, std::numeric_limits<unsigned long long>::max()) {
     unknown_dso_ = Dso::CreateDso(DSO_ELF_FILE, "unknown");
     unknown_map_ =
-        MapEntry(0, std::numeric_limits<unsigned long long>::max(), 0, 0, unknown_dso_.get());
+        MapEntry(0, std::numeric_limits<unsigned long long>::max(), 0, 0, unknown_dso_.get(), false);
+    kernel_dso_ = Dso::CreateDso(DSO_KERNEL);
   }
 
   void AddThread(int pid, int tid, const std::string& comm);
index 2eaac42..0068b8e 100644 (file)
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -259,3 +260,44 @@ bool IsRoot() {
   }
   return is_root == 1;
 }
+
+bool ProcessKernelSymbols(std::string& symbol_data,
+                          std::function<bool(const KernelSymbol&)> callback) {
+  char* p = &symbol_data[0];
+  char* data_end = p + symbol_data.size();
+  while (p < data_end) {
+    char* line_end = strchr(p, '\n');
+    if (line_end != nullptr) {
+      *line_end = '\0';
+    }
+    size_t line_size = (line_end != nullptr) ? (line_end - p) : (data_end - p);
+    // Parse line like: ffffffffa005c4e4 d __warned.41698       [libsas]
+    char name[line_size];
+    char module[line_size];
+    strcpy(module, "");
+
+    KernelSymbol symbol;
+    int ret = sscanf(p, "%" PRIx64 " %c %s%s", &symbol.addr, &symbol.type, name, module);
+    if (line_end != nullptr) {
+      *line_end = '\n';
+      p = line_end + 1;
+    } else {
+      p = data_end;
+    }
+    if (ret >= 3) {
+      symbol.name = name;
+      size_t module_len = strlen(module);
+      if (module_len > 2 && module[0] == '[' && module[module_len - 1] == ']') {
+        module[module_len - 1] = '\0';
+        symbol.module = &module[1];
+      } else {
+        symbol.module = nullptr;
+      }
+
+      if (callback(symbol)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
index 1f02e17..fc47485 100644 (file)
@@ -125,4 +125,14 @@ bool GetLogSeverity(const std::string& name, android::base::LogSeverity* severit
 
 bool IsRoot();
 
+struct KernelSymbol {
+  uint64_t addr;
+  char type;
+  const char* name;
+  const char* module;  // If nullptr, the symbol is not in a kernel module.
+};
+
+bool ProcessKernelSymbols(std::string& symbol_data,
+                          std::function<bool(const KernelSymbol&)> callback);
+
 #endif  // SIMPLE_PERF_UTILS_H_
diff --git a/simpleperf/utils_test.cpp b/simpleperf/utils_test.cpp
new file mode 100644 (file)
index 0000000..23c669e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "utils.h"
+
+static bool ModulesMatch(const char* p, const char* q) {
+  if (p == nullptr && q == nullptr) {
+    return true;
+  }
+  if (p != nullptr && q != nullptr) {
+    return strcmp(p, q) == 0;
+  }
+  return false;
+}
+
+static bool KernelSymbolsMatch(const KernelSymbol& sym1,
+                               const KernelSymbol& sym2) {
+  return sym1.addr == sym2.addr && sym1.type == sym2.type &&
+         strcmp(sym1.name, sym2.name) == 0 &&
+         ModulesMatch(sym1.module, sym2.module);
+}
+
+TEST(environment, ProcessKernelSymbols) {
+  std::string data =
+      "ffffffffa005c4e4 d __warned.41698   [libsas]\n"
+      "aaaaaaaaaaaaaaaa T _text\n"
+      "cccccccccccccccc c ccccc\n";
+  KernelSymbol expected_symbol;
+  expected_symbol.addr = 0xffffffffa005c4e4ULL;
+  expected_symbol.type = 'd';
+  expected_symbol.name = "__warned.41698";
+  expected_symbol.module = "libsas";
+  ASSERT_TRUE(ProcessKernelSymbols(
+      data,
+      std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
+
+  expected_symbol.addr = 0xaaaaaaaaaaaaaaaaULL;
+  expected_symbol.type = 'T';
+  expected_symbol.name = "_text";
+  expected_symbol.module = nullptr;
+  ASSERT_TRUE(ProcessKernelSymbols(
+      data,
+      std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
+
+  expected_symbol.name = "non_existent_symbol";
+  ASSERT_FALSE(ProcessKernelSymbols(
+      data,
+      std::bind(&KernelSymbolsMatch, std::placeholders::_1, expected_symbol)));
+}