OSDN Git Service

Support to use kallsyms
authorDaniel Friederich <dfriederich@magicleap.com>
Mon, 17 Oct 2016 17:28:03 +0000 (12:28 -0500)
committerDaniel Friederich <dfriederich@magicleap.com>
Wed, 16 Nov 2016 16:53:25 +0000 (10:53 -0600)
Also:
- Adapt to use with Python 3
  (where str is a wide type but our C API's expect 8 bit character strings)
- Use OS specific so names (e.g. simpleperf_report.dll on Windows)
- On Windows as we use mingw to build, preload libwinpthread-1.dll.

Test: with manual incovation using report_sample.py
Change-Id: Id973c463608c520b8eec229026c74dc5e8144cf8

simpleperf/Android.mk
simpleperf/cmd_record_test.cpp
simpleperf/report_lib_interface.cpp
simpleperf/scripts/libsimpleperf_report.so
simpleperf/scripts/report_sample.py
simpleperf/scripts/simpleperf_report_lib.py
simpleperf/thread_tree.cpp
simpleperf/thread_tree.h

index 3c40292..314a606 100644 (file)
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
 LOCAL_PATH := $(call my-dir)
 
 simpleperf_version :=  $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)
@@ -154,7 +153,7 @@ LOCAL_SRC_FILES_windows := $(libsimpleperf_src_files_windows)
 LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_host)
 LOCAL_STATIC_LIBRARIES_linux := $(simpleperf_static_libraries_host_linux)
 LOCAL_LDLIBS_linux := $(simpleperf_ldlibs_host_linux)
-LOCAL_MULTILIB := first
+LOCAL_MULTILIB := both
 LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
 LOCAL_CXX_STL := libc++_static
 include $(LLVM_HOST_BUILD_MK)
@@ -231,7 +230,7 @@ LOCAL_SRC_FILES := report_lib_interface.cpp
 LOCAL_STATIC_LIBRARIES := libsimpleperf $(simpleperf_static_libraries_host)
 LOCAL_STATIC_LIBRARIES_linux := $(simpleperf_static_libraries_host_linux)
 LOCAL_LDLIBS_linux := $(simpleperf_ldlibs_host_linux) -Wl,--exclude-libs,ALL
-LOCAL_MULTILIB := first
+LOCAL_MULTILIB := both
 LOCAL_CXX_STL := libc++_static
 include $(LLVM_HOST_BUILD_MK)
 include $(BUILD_HOST_SHARED_LIBRARY)
index 07cf930..b863f01 100644 (file)
@@ -88,7 +88,8 @@ TEST(record_cmd, dump_kernel_mmap) {
     if (record->type() == PERF_RECORD_MMAP) {
       const MmapRecord* mmap_record =
           static_cast<const MmapRecord*>(record.get());
-      if (strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME) == 0) {
+      if (strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME) == 0 ||
+          strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME_PERF) == 0) {
         have_kernel_mmap = true;
         break;
       }
index cdd9fb1..2aa0ccc 100644 (file)
  */
 
 #include <memory>
+#include <utility>
 
 #include <android-base/logging.h>
+#include <android-base/file.h>
 
 #include "dso.h"
 #include "event_attr.h"
@@ -71,6 +73,7 @@ void DestroyReportLib(ReportLib* report_lib) EXPORT;
 bool SetLogSeverity(ReportLib* report_lib, const char* log_level) EXPORT;
 bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) EXPORT;
 bool SetRecordFile(ReportLib* report_lib, const char* record_file) EXPORT;
+bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) EXPORT;
 void ShowIpForUnknownSymbol(ReportLib* report_lib) EXPORT;
 
 Sample* GetNextSample(ReportLib* report_lib) EXPORT;
@@ -110,6 +113,8 @@ class ReportLib {
     return true;
   }
 
+  bool SetKallsymsFile(const char* kallsyms_file);
+
   void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); }
 
   Sample* GetNextSample();
@@ -146,6 +151,17 @@ bool ReportLib::SetLogSeverity(const char* log_level) {
   return true;
 }
 
+bool ReportLib::SetKallsymsFile(const char* kallsyms_file) {
+  std::string kallsyms;
+  if (!android::base::ReadFileToString(kallsyms_file, &kallsyms)) {
+    LOG(WARNING) << "Failed to read in kallsyms file from " << kallsyms_file;
+    return false;
+  }
+  Dso::SetKallsyms(std::move(kallsyms));
+  return true;
+}
+
+
 Sample* ReportLib::GetNextSample() {
   if (record_file_reader_ == nullptr) {
     record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
@@ -301,6 +317,10 @@ void ShowIpForUnknownSymbol(ReportLib* report_lib) {
   return report_lib->ShowIpForUnknownSymbol();
 }
 
+bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) {
+  return report_lib->SetKallsymsFile(kallsyms_file);
+}
+
 Sample* GetNextSample(ReportLib* report_lib) {
   return report_lib->GetNextSample();
 }
index a31c8e5..ee3bff3 100755 (executable)
Binary files a/simpleperf/scripts/libsimpleperf_report.so and b/simpleperf/scripts/libsimpleperf_report.so differ
index 831baeb..7e73326 100644 (file)
@@ -23,13 +23,14 @@ from simpleperf_report_lib import *
 
 
 def usage():
-    print 'python report_sample.py [options] <record_file>'
-    print '-h/--help print this help message'
-    print '--symfs <symfs_dir>  Set the path to looking for symbols'
-    print 'If record file is not given, use default file perf.data.'
+    print('python report_sample.py [options] <record_file>')
+    print('-h/--help print this help message')
+    print('--symfs <symfs_dir>  Set the path to looking for symbols')
+    print('--kallsyms <kallsyms_file>  Set the path to a kallsyms file')
+    print('If record file is not given, use default file perf.data.')
 
 
-def report_sample(record_file, symfs_dir):
+def report_sample(record_file, symfs_dir, kallsyms_file=None):
     """ read record_file, and print each sample"""
     lib = ReportLib()
 
@@ -38,6 +39,8 @@ def report_sample(record_file, symfs_dir):
         lib.SetSymfs(symfs_dir)
     if record_file is not None:
         lib.SetRecordFile(record_file)
+    if kallsyms_file is not None:
+        lib.SetKallsymsFile(kallsyms_file)
 
     while True:
         sample = lib.GetNextSample()
@@ -50,17 +53,18 @@ def report_sample(record_file, symfs_dir):
 
         sec = sample[0].time / 1000000000
         usec = (sample[0].time - sec * 1000000000) / 1000
-        print '%s\t%d [%03d] %d.%d:\t\t%d %s:' % (sample[0].thread_comm, sample[0].tid, sample[0].cpu, sec, usec, sample[0].period, event[0].name)
-        print '%16x\t%s (%s)' % (sample[0].ip, symbol[0].symbol_name, symbol[0].dso_name)
+        print('%s\t%d [%03d] %d.%d:\t\t%d %s:' % (sample[0].thread_comm, sample[0].tid, sample[0].cpu, sec, usec, sample[0].period, event[0].name))
+        print('%16x\t%s (%s)' % (sample[0].ip, symbol[0].symbol_name, symbol[0].dso_name))
         for i in range(callchain[0].nr):
             entry = callchain[0].entries[i]
-            print '%16x\t%s (%s)' % (entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name)
-        print
+            print('%16x\t%s (%s)' % (entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name))
+        print('')
 
 
 if __name__ == '__main__':
     record_file = 'perf.data'
     symfs_dir = None
+    kallsyms_file = None
     i = 1
     while i < len(sys.argv):
         if sys.argv[i] == '-h' or sys.argv[i] == '--help':
@@ -71,10 +75,17 @@ if __name__ == '__main__':
                 symfs_dir = sys.argv[i + 1]
                 i += 1
             else:
-                print 'argument for --symfs is missing'
+                print('argument for --symfs is missing')
+                sys.exit(1)
+        elif sys.argv[i] == '--kallsyms':
+            if i + 1 < len(sys.argv):
+                kallsyms_file = sys.argv[i + 1]
+                i += 1
+            else:
+                print('argument for --kallsyms is missing')
                 sys.exit(1)
         else:
           record_file = sys.argv[i]
         i += 1
 
-    report_sample(record_file, symfs_dir)
+    report_sample(record_file, symfs_dir, kallsyms_file)
index 8b0e133..05ad8f0 100644 (file)
 
 import ctypes as ct
 import os
+import sys
+
+
+def _isWindows():
+    return sys.platform == 'win32' or sys.platform == 'cygwin'
 
 
 def _get_script_path():
     return os.path.dirname(os.path.realpath(__file__))
 
 
+def _get_native_lib():
+    if _isWindows():
+        so_name = 'libsimpleperf_report.dll'
+    elif sys.platform == 'darwin': # OSX
+        so_name = 'libsimpleperf_report.dylib'
+    else:
+        so_name = 'libsimpleperf_report.so'
+
+    return os.path.join(_get_script_path(), so_name)
+
+
 def _is_null(p):
     return ct.cast(p, ct.c_void_p).value is None
 
 
+def _char_pt(str):
+    if sys.version_info < (3, 0):
+        return str
+    # In python 3, str are wide strings whereas the C api expects 8 bit strings, hence we have to convert
+    # For now using utf-8 as the encoding.
+    return str.encode('utf-8')
+
+
 class SampleStruct(ct.Structure):
     _fields_ = [('ip', ct.c_uint64),
                 ('pid', ct.c_uint32),
@@ -64,11 +88,14 @@ class CallChainStructure(ct.Structure):
 class ReportLibStructure(ct.Structure):
     _fields_ = []
 
+
 class ReportLib(object):
 
     def __init__(self, native_lib_path=None):
         if native_lib_path is None:
-            native_lib_path = _get_script_path() + "/libsimpleperf_report.so"
+            native_lib_path = _get_native_lib()
+
+        self._load_dependent_lib()
         self._lib = ct.CDLL(native_lib_path)
         self._CreateReportLibFunc = self._lib.CreateReportLib
         self._CreateReportLibFunc.restype = ct.POINTER(ReportLibStructure)
@@ -76,6 +103,7 @@ class ReportLib(object):
         self._SetLogSeverityFunc = self._lib.SetLogSeverity
         self._SetSymfsFunc = self._lib.SetSymfs
         self._SetRecordFileFunc = self._lib.SetRecordFile
+        self._SetKallsymsFileFunc = self._lib.SetKallsymsFile
         self._ShowIpForUnknownSymbolFunc = self._lib.ShowIpForUnknownSymbol
         self._GetNextSampleFunc = self._lib.GetNextSample
         self._GetNextSampleFunc.restype = ct.POINTER(SampleStruct)
@@ -89,6 +117,14 @@ class ReportLib(object):
         self._instance = self._CreateReportLibFunc()
         assert(not _is_null(self._instance))
 
+    def _load_dependent_lib(self):
+        # As the windows dll is built with mingw we need to also find "libwinpthread-1.dll".
+        # Load it before libsimpleperf_report.dll if it does exist in the same folder as this script.
+        if _isWindows():
+            libwinpthread_path = os.path.join(_get_script_path(), "libwinpthread-1.dll")
+            if os.path.exists(libwinpthread_path):
+                self._libwinpthread = ct.CDLL(libwinpthread_path)
+
     def Close(self):
         if self._instance is None:
             return
@@ -97,22 +133,27 @@ class ReportLib(object):
 
     def SetLogSeverity(self, log_level='info'):
         """ Set log severity of native lib, can be verbose,debug,info,error,fatal."""
-        cond = self._SetLogSeverityFunc(self.getInstance(), log_level)
-        assert(cond)
+        cond = self._SetLogSeverityFunc(self.getInstance(), _char_pt(log_level))
+        self._check(cond, "Failed to set log level")
 
     def SetSymfs(self, symfs_dir):
         """ Set directory used to find symbols."""
-        cond = self._SetSymfsFunc(self.getInstance(), symfs_dir)
-        assert(cond)
+        cond = self._SetSymfsFunc(self.getInstance(), _char_pt(symfs_dir))
+        self._check(cond, "Failed to set symbols directory")
 
     def SetRecordFile(self, record_file):
         """ Set the path of record file, like perf.data."""
-        cond = self._SetRecordFileFunc(self.getInstance(), record_file)
-        assert(cond)
+        cond = self._SetRecordFileFunc(self.getInstance(), _char_pt(record_file))
+        self._check(cond, "Failed to set record file")
 
     def ShowIpForUnknownSymbol(self):
         self._ShowIpForUnknownSymbolFunc(self.getInstance())
 
+    def SetKallsymsFile(self, kallsym_file):
+        """ Set the file path to a copy of the /proc/kallsyms file (for off device decoding) """
+        cond = self._SetKallsymsFileFunc(self.getInstance(), _char_pt(kallsym_file))
+        self._check(cond, "Failed to set kallsyms file")
+
     def GetNextSample(self):
         sample = self._GetNextSampleFunc(self.getInstance())
         if _is_null(sample):
@@ -138,3 +179,7 @@ class ReportLib(object):
         if self._instance is None:
             raise Exception("Instance is Closed")
         return self._instance
+
+    def _check(self, cond, failmsg):
+        if not cond:
+            raise Exception(failmsg)
index 84cfb59..09b0e1f 100644 (file)
@@ -103,7 +103,8 @@ 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 (filename == DEFAULT_KERNEL_MMAP_NAME ||
+      filename == DEFAULT_KERNEL_MMAP_NAME_PERF) {
     return kernel_dso_.get();
   }
   auto it = module_dso_tree_.find(filename);
index 3108fe4..d7566d4 100644 (file)
@@ -29,7 +29,8 @@
 struct Record;
 
 constexpr char DEFAULT_KERNEL_MMAP_NAME[] = "[kernel.kallsyms]";
-
+// Seen in perf.data file generated by perf.
+constexpr char DEFAULT_KERNEL_MMAP_NAME_PERF[] = "[kernel.kallsyms]_text";
 constexpr char DEFAULT_EXECNAME_FOR_THREAD_MMAP[] = "//anon";
 
 namespace simpleperf {