# 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)
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)
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)
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;
}
*/
#include <memory>
+#include <utility>
#include <android-base/logging.h>
+#include <android-base/file.h>
#include "dso.h"
#include "event_attr.h"
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;
return true;
}
+ bool SetKallsymsFile(const char* kallsyms_file);
+
void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); }
Sample* GetNextSample();
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_);
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();
}
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()
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()
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':
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)
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),
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)
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)
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
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):
if self._instance is None:
raise Exception("Instance is Closed")
return self._instance
+
+ def _check(self, cond, failmsg):
+ if not cond:
+ raise Exception(failmsg)
}
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);
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 {