summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2017-07-25 15:08:05 -0700
committerYabin Cui <yabinc@google.com>2017-07-25 15:08:05 -0700
commit33275782e8c0fe3f7116236c5353beb0914b4d30 (patch)
tree206e8bfa8239b9e26d3898a36ab3bb6e6babf5b1
parent60c6d5bf601f0bbcf5235996f2d7e3aa6b3bb2dd (diff)
downloadextras-33275782e8c0fe3f7116236c5353beb0914b4d30.tar.gz
simpleperf: support reporting trace offcpu data in simpleperf_report_lib.
Export python interface for reading meta info. Change sample.period to sample time difference when trace_offcpu is true. Add unit tests. Bug: http://b/37572306 Test: run python test.py. Change-Id: Ic144314184d115cc55630d3c69b66a0d8594721a
-rw-r--r--simpleperf/report_lib_interface.cpp72
-rw-r--r--simpleperf/scripts/simpleperf_report_lib.py101
-rw-r--r--simpleperf/scripts/test.py76
-rw-r--r--simpleperf/scripts/testdata/perf_with_symbols.databin0 -> 54797 bytes
-rw-r--r--simpleperf/scripts/testdata/perf_with_trace_offcpu.databin0 -> 197611 bytes
5 files changed, 175 insertions, 74 deletions
diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp
index 0d4380f6..79a2103c 100644
--- a/simpleperf/report_lib_interface.cpp
+++ b/simpleperf/report_lib_interface.cpp
@@ -22,6 +22,7 @@
#include "dso.h"
#include "event_attr.h"
+#include "event_type.h"
#include "record_file.h"
#include "thread_tree.h"
#include "utils.h"
@@ -71,6 +72,11 @@ struct CallChain {
CallChainEntry* entries;
};
+struct MetaInfoEntry {
+ const char* key;
+ const char* value;
+};
+
// Create a new instance,
// pass the instance to the other functions below.
ReportLib* CreateReportLib() EXPORT;
@@ -90,6 +96,7 @@ SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) EXPORT;
CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) EXPORT;
const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) EXPORT;
+MetaInfoEntry* GetNextMetaInfo(ReportLib* report_lib) EXPORT;
}
struct EventAttrWithName {
@@ -111,8 +118,10 @@ class ReportLib {
new android::base::ScopedLogSeverity(android::base::INFO)),
record_filename_("perf.data"),
current_thread_(nullptr),
- update_flag_(0)
- {}
+ update_flag_(0),
+ trace_offcpu_(false) {
+ current_meta_info_.key = current_meta_info_.value = nullptr;
+ }
bool SetLogSeverity(const char* log_level);
@@ -133,6 +142,7 @@ class ReportLib {
CallChain* GetCallChainOfCurrentSample();
const char* GetBuildIdForPath(const char* path);
+ MetaInfoEntry* GetNextMetaInfo();
private:
Sample* GetCurrentSample();
@@ -154,6 +164,12 @@ class ReportLib {
std::string build_id_string_;
int update_flag_;
std::vector<EventAttrWithName> event_attrs_;
+
+ std::unordered_map<std::string, std::string> meta_info_map_;
+ MetaInfoEntry current_meta_info_;
+ std::unique_ptr<ScopedEventTypes> scoped_event_types_;
+ bool trace_offcpu_;
+ std::unordered_map<pid_t, std::unique_ptr<SampleRecord>> next_sample_cache_;
};
bool ReportLib::SetLogSeverity(const char* log_level) {
@@ -184,6 +200,18 @@ bool ReportLib::OpenRecordFileIfNecessary() {
return false;
}
record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
+ if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO) &&
+ !record_file_reader_->ReadMetaInfoFeature(&meta_info_map_)) {
+ return false;
+ }
+ auto it = meta_info_map_.find("event_type_info");
+ if (it != meta_info_map_.end()) {
+ scoped_event_types_.reset(new ScopedEventTypes(it->second));
+ }
+ it = meta_info_map_.find("trace_offcpu");
+ if (it != meta_info_map_.end()) {
+ trace_offcpu_ = it->second == "true";
+ }
}
return true;
}
@@ -202,6 +230,17 @@ Sample* ReportLib::GetNextSample() {
}
thread_tree_.Update(*record);
if (record->type() == PERF_RECORD_SAMPLE) {
+ if (trace_offcpu_) {
+ SampleRecord* r = static_cast<SampleRecord*>(record.release());
+ auto it = next_sample_cache_.find(r->tid_data.tid);
+ if (it == next_sample_cache_.end()) {
+ next_sample_cache_[r->tid_data.tid].reset(r);
+ continue;
+ } else {
+ record.reset(it->second.release());
+ it->second.reset(r);
+ }
+ }
current_record_.reset(static_cast<SampleRecord*>(record.release()));
break;
}
@@ -223,7 +262,13 @@ Sample* ReportLib::GetCurrentSample() {
current_sample_.time = r.time_data.time;
current_sample_.in_kernel = r.InKernel();
current_sample_.cpu = r.cpu_data.cpu;
- current_sample_.period = r.period_data.period;
+ if (trace_offcpu_) {
+ uint64_t next_time = std::max(next_sample_cache_[r.tid_data.tid]->time_data.time,
+ r.time_data.time + 1);
+ current_sample_.period = next_time - r.time_data.time;
+ } else {
+ current_sample_.period = r.period_data.period;
+ }
update_flag_ |= UPDATE_FLAG_OF_SAMPLE;
}
return &current_sample_;
@@ -342,6 +387,23 @@ const char* ReportLib::GetBuildIdForPath(const char* path) {
return build_id_string_.c_str();
}
+MetaInfoEntry* ReportLib::GetNextMetaInfo() {
+ if (!OpenRecordFileIfNecessary()) {
+ return nullptr;
+ }
+ auto it = meta_info_map_.begin();
+ if (current_meta_info_.key != nullptr) {
+ it = meta_info_map_.find(current_meta_info_.key);
+ ++it;
+ }
+ if (it == meta_info_map_.end()) {
+ return nullptr;
+ }
+ current_meta_info_.key = it->first.c_str();
+ current_meta_info_.value = it->second.c_str();
+ return &current_meta_info_;
+}
+
// Exported methods working with a client created instance
ReportLib* CreateReportLib() {
return new ReportLib();
@@ -390,3 +452,7 @@ CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) {
const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) {
return report_lib->GetBuildIdForPath(path);
}
+
+MetaInfoEntry* GetNextMetaInfo(ReportLib* report_lib) {
+ return report_lib->GetNextMetaInfo();
+}
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
index c540059e..67fdfa3e 100644
--- a/simpleperf/scripts/simpleperf_report_lib.py
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -83,6 +83,11 @@ class CallChainStructure(ct.Structure):
('entries', ct.POINTER(CallChainEntryStructure))]
+class MetaInfoEntryStructure(ct.Structure):
+ _fields_ = [('key', ct.c_char_p),
+ ('value', ct.c_char_p)]
+
+
# convert char_p to str for python3.
class SampleStructUsingStr(object):
def __init__(self, sample):
@@ -155,10 +160,14 @@ class ReportLib(object):
CallChainStructure)
self._GetBuildIdForPathFunc = self._lib.GetBuildIdForPath
self._GetBuildIdForPathFunc.restype = ct.c_char_p
+ self._GetNextMetaInfoFunc = self._lib.GetNextMetaInfo
+ self._GetNextMetaInfoFunc.restype = ct.POINTER(MetaInfoEntryStructure)
self._instance = self._CreateReportLibFunc()
assert(not _is_null(self._instance))
self.convert_to_str = (sys.version_info >= (3, 0))
+ self.meta_info = None
+ self.current_sample = None
def _load_dependent_lib(self):
# As the windows dll is built with mingw we need to load "libwinpthread-1.dll".
@@ -195,12 +204,16 @@ class ReportLib(object):
self._check(cond, "Failed to set kallsyms file")
def GetNextSample(self):
- sample = self._GetNextSampleFunc(self.getInstance())
- if _is_null(sample):
- return None
- if self.convert_to_str:
- return SampleStructUsingStr(sample[0])
- return sample[0]
+ psample = self._GetNextSampleFunc(self.getInstance())
+ if _is_null(psample):
+ self.current_sample = None
+ else:
+ sample = psample[0]
+ self.current_sample = SampleStructUsingStr(sample) if self.convert_to_str else sample
+ return self.current_sample
+
+ def GetCurrentSample(self):
+ return self.current_sample
def GetEventOfCurrentSample(self):
event = self._GetEventOfCurrentSampleFunc(self.getInstance())
@@ -228,6 +241,17 @@ class ReportLib(object):
assert(not _is_null(build_id))
return _char_pt_to_str(build_id)
+ def MetaInfo(self):
+ if self.meta_info is None:
+ self.meta_info = {}
+ while True:
+ entry = self._GetNextMetaInfoFunc(self.getInstance())
+ if _is_null(entry): break
+ key = _char_pt_to_str(entry[0].key)
+ value = _char_pt_to_str(entry[0].value)
+ self.meta_info[key] = value
+ return self.meta_info
+
def getInstance(self):
if self._instance is None:
raise Exception("Instance is Closed")
@@ -236,68 +260,3 @@ class ReportLib(object):
def _check(self, cond, failmsg):
if not cond:
raise Exception(failmsg)
-
-
-class TestReportLib(unittest.TestCase):
- def setUp(self):
- self.perf_data_path = os.path.join(os.path.dirname(get_script_dir()),
- 'testdata', 'perf_with_symbols.data')
- if not os.path.isfile(self.perf_data_path):
- raise Exception("can't find perf_data at %s" % self.perf_data_path)
- self.report_lib = ReportLib()
- self.report_lib.SetRecordFile(self.perf_data_path)
-
- def tearDown(self):
- self.report_lib.Close()
-
- def test_build_id(self):
- build_id = self.report_lib.GetBuildIdForPath('/data/t2')
- self.assertEqual(build_id, '0x70f1fe24500fc8b0d9eb477199ca1ca21acca4de')
-
- def test_symbol_addr(self):
- found_func2 = False
- while True:
- sample = self.report_lib.GetNextSample()
- if sample is None:
- break
- symbol = self.report_lib.GetSymbolOfCurrentSample()
- if symbol.symbol_name == 'func2(int, int)':
- found_func2 = True
- self.assertEqual(symbol.symbol_addr, 0x4004ed)
- self.assertTrue(found_func2)
-
- def test_sample(self):
- found_sample = False
- while True:
- sample = self.report_lib.GetNextSample()
- if sample is None:
- break
- if sample.ip == 0x4004ff and sample.time == 7637889424953:
- found_sample = True
- self.assertEqual(sample.pid, 15926)
- self.assertEqual(sample.tid, 15926)
- self.assertEqual(sample.thread_comm, 't2')
- self.assertEqual(sample.cpu, 5)
- self.assertEqual(sample.period, 694614)
- event = self.report_lib.GetEventOfCurrentSample()
- self.assertEqual(event.name, 'cpu-cycles')
- callchain = self.report_lib.GetCallChainOfCurrentSample()
- self.assertEqual(callchain.nr, 0)
- self.assertTrue(found_sample)
-
-
-def main():
- test_all = True
- if len(sys.argv) > 1 and sys.argv[1] == '--test-one':
- test_all = False
- del sys.argv[1]
-
- if test_all:
- subprocess.check_call(['python', os.path.realpath(__file__), '--test-one'])
- subprocess.check_call(['python3', os.path.realpath(__file__), '--test-one'])
- else:
- sys.exit(unittest.main())
-
-
-if __name__ == '__main__':
- main() \ No newline at end of file
diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py
index 27327d96..3bc50358 100644
--- a/simpleperf/scripts/test.py
+++ b/simpleperf/scripts/test.py
@@ -41,6 +41,7 @@ import sys
import tempfile
import unittest
from utils import *
+from simpleperf_report_lib import ReportLib
has_google_protobuf = True
try:
@@ -418,6 +419,81 @@ class TestExampleOfKotlinRoot(TestExamplesBase):
self.common_test_app_profiler()
+class TestReportLib(unittest.TestCase):
+ def setUp(self):
+ self.report_lib = ReportLib()
+ self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_symbols.data'))
+
+ def tearDown(self):
+ self.report_lib.Close()
+
+ def test_build_id(self):
+ build_id = self.report_lib.GetBuildIdForPath('/data/t2')
+ self.assertEqual(build_id, '0x70f1fe24500fc8b0d9eb477199ca1ca21acca4de')
+
+ def test_symbol_addr(self):
+ found_func2 = False
+ while self.report_lib.GetNextSample():
+ sample = self.report_lib.GetCurrentSample()
+ symbol = self.report_lib.GetSymbolOfCurrentSample()
+ if symbol.symbol_name == 'func2(int, int)':
+ found_func2 = True
+ self.assertEqual(symbol.symbol_addr, 0x4004ed)
+ self.assertTrue(found_func2)
+
+ def test_sample(self):
+ found_sample = False
+ while self.report_lib.GetNextSample():
+ sample = self.report_lib.GetCurrentSample()
+ if sample.ip == 0x4004ff and sample.time == 7637889424953:
+ found_sample = True
+ self.assertEqual(sample.pid, 15926)
+ self.assertEqual(sample.tid, 15926)
+ self.assertEqual(sample.thread_comm, 't2')
+ self.assertEqual(sample.cpu, 5)
+ self.assertEqual(sample.period, 694614)
+ event = self.report_lib.GetEventOfCurrentSample()
+ self.assertEqual(event.name, 'cpu-cycles')
+ callchain = self.report_lib.GetCallChainOfCurrentSample()
+ self.assertEqual(callchain.nr, 0)
+ self.assertTrue(found_sample)
+
+ def test_meta_info(self):
+ self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
+ meta_info = self.report_lib.MetaInfo()
+ self.assertEqual(meta_info["simpleperf_version"], "1.65f91c7ed862")
+ self.assertEqual(meta_info["system_wide_collection"], "false")
+ self.assertEqual(meta_info["trace_offcpu"], "true")
+ self.assertEqual(meta_info["event_type_info"], "cpu-cycles,0,0\nsched:sched_switch,2,47")
+
+ def test_event_name_from_meta_info(self):
+ self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
+ event_names = set()
+ while self.report_lib.GetNextSample():
+ event_names.add(self.report_lib.GetEventOfCurrentSample().name)
+ self.assertTrue('sched:sched_switch' in event_names)
+ self.assertTrue('cpu-cycles' in event_names)
+
+ def test_offcpu(self):
+ self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
+ total_period = 0
+ sleep_function_period = 0
+ sleep_function_name = "SleepFunction(unsigned long long)"
+ while self.report_lib.GetNextSample():
+ sample = self.report_lib.GetCurrentSample()
+ total_period += sample.period
+ if self.report_lib.GetSymbolOfCurrentSample().symbol_name == sleep_function_name:
+ sleep_function_period += sample.period
+ continue
+ callchain = self.report_lib.GetCallChainOfCurrentSample()
+ for i in range(callchain.nr):
+ if callchain.entries[i].symbol.symbol_name == sleep_function_name:
+ sleep_function_period += sample.period
+ break
+ sleep_percentage = float(sleep_function_period) / total_period
+ self.assertAlmostEqual(sleep_percentage, 0.4629, delta=0.0001)
+
+
if __name__ == '__main__':
test_program = unittest.main(failfast=True, exit=False)
if test_program.result.wasSuccessful():
diff --git a/simpleperf/scripts/testdata/perf_with_symbols.data b/simpleperf/scripts/testdata/perf_with_symbols.data
new file mode 100644
index 00000000..ca74d159
--- /dev/null
+++ b/simpleperf/scripts/testdata/perf_with_symbols.data
Binary files differ
diff --git a/simpleperf/scripts/testdata/perf_with_trace_offcpu.data b/simpleperf/scripts/testdata/perf_with_trace_offcpu.data
new file mode 100644
index 00000000..6d0c5e0a
--- /dev/null
+++ b/simpleperf/scripts/testdata/perf_with_trace_offcpu.data
Binary files differ