summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2024-01-29 16:14:05 -0800
committerYabin Cui <yabinc@google.com>2024-02-02 17:20:41 -0800
commitbbe9f034ab0be65bca43429c6aee5f6c14fb95a9 (patch)
tree516ed200cb1b3dd0d07772771fe604abcc3c5c37 /simpleperf
parent55117049fff502fa3c668fcd82f75eb301046800 (diff)
downloadextras-bbe9f034ab0be65bca43429c6aee5f6c14fb95a9.tar.gz
simpleperf: gecko_profile_generator.py: Add --percpu-samples
It is to show sample distribution for each cpu: 1) Use cpu ids as thread names. 2) In each sample, add thread name + process name as the root of the call stack. Bug: 323605908 Test: run test.py --only-host-test Change-Id: Ie6e363727069876d8093bedd93c37daeb0a76cf0
Diffstat (limited to 'simpleperf')
-rwxr-xr-xsimpleperf/scripts/gecko_profile_generator.py72
1 files changed, 50 insertions, 22 deletions
diff --git a/simpleperf/scripts/gecko_profile_generator.py b/simpleperf/scripts/gecko_profile_generator.py
index d09a00b1..980012c0 100755
--- a/simpleperf/scripts/gecko_profile_generator.py
+++ b/simpleperf/scripts/gecko_profile_generator.py
@@ -390,7 +390,8 @@ def _gecko_profile(
symfs_dir: Optional[str],
kallsyms_file: Optional[str],
report_lib_options: ReportLibOptions,
- max_remove_gap_length: int) -> GeckoProfile:
+ max_remove_gap_length: int,
+ percpu_samples: bool) -> GeckoProfile:
"""convert a simpleperf profile to gecko format"""
lib = GetReportLib(record_file)
@@ -399,6 +400,10 @@ def _gecko_profile(
lib.SetSymfs(symfs_dir)
if kallsyms_file is not None:
lib.SetKallsymsFile(kallsyms_file)
+ if percpu_samples:
+ # Grouping samples by cpus doesn't support off cpu samples.
+ if lib.GetSupportedTraceOffCpuModes():
+ report_lib_options.trace_offcpu = 'on-cpu'
lib.SetReportOptions(report_lib_options)
arch = lib.GetArch()
@@ -406,7 +411,9 @@ def _gecko_profile(
record_cmd = lib.GetRecordCmd()
# Map from tid to Thread
- threadMap: Dict[int, Thread] = {}
+ thread_map: Dict[int, Thread] = {}
+ # Map from pid to process name
+ process_names: Dict[int, str] = {}
while True:
sample = lib.GetNextSample()
@@ -424,28 +431,44 @@ def _gecko_profile(
# We want root first, leaf last.
stack.reverse()
- # add thread sample
- thread = threadMap.get(sample.tid)
- if thread is None:
- thread = Thread(comm=sample.thread_comm, pid=sample.pid, tid=sample.tid)
- threadMap[sample.tid] = thread
- thread.add_sample(
- comm=sample.thread_comm,
- stack=stack,
- # We are being a bit fast and loose here with time here. simpleperf
- # uses CLOCK_MONOTONIC by default, which doesn't use the normal unix
- # epoch, but rather some arbitrary time. In practice, this doesn't
- # matter, the Firefox Profiler normalises all the timestamps to begin at
- # the minimum time. Consider fixing this in future, if needed, by
- # setting `simpleperf record --clockid realtime`.
- time_ms=sample_time_ms)
-
- for thread in threadMap.values():
+ if percpu_samples:
+ if sample.tid == sample.pid:
+ process_names[sample.pid] = sample.thread_comm
+ process_name = process_names.get(sample.pid)
+ stack = [
+ '%s tid %d (in %s pid %d)' %
+ (sample.thread_comm, sample.tid, process_name, sample.pid)] + stack
+ thread = thread_map.get(sample.cpu)
+ if thread is None:
+ thread = Thread(comm=f'Cpu {sample.cpu}', pid=sample.cpu, tid=sample.cpu)
+ thread_map[sample.cpu] = thread
+ thread.add_sample(
+ comm=f'Cpu {sample.cpu}',
+ stack=stack,
+ time_ms=sample_time_ms)
+ else:
+ # add thread sample
+ thread = thread_map.get(sample.tid)
+ if thread is None:
+ thread = Thread(comm=sample.thread_comm, pid=sample.pid, tid=sample.tid)
+ thread_map[sample.tid] = thread
+ thread.add_sample(
+ comm=sample.thread_comm,
+ stack=stack,
+ # We are being a bit fast and loose here with time here. simpleperf
+ # uses CLOCK_MONOTONIC by default, which doesn't use the normal unix
+ # epoch, but rather some arbitrary time. In practice, this doesn't
+ # matter, the Firefox Profiler normalises all the timestamps to begin at
+ # the minimum time. Consider fixing this in future, if needed, by
+ # setting `simpleperf record --clockid realtime`.
+ time_ms=sample_time_ms)
+
+ for thread in thread_map.values():
thread.sort_samples()
- remove_stack_gaps(max_remove_gap_length, threadMap)
+ remove_stack_gaps(max_remove_gap_length, thread_map)
- threads = [thread.to_json_dict() for thread in threadMap.values()]
+ threads = [thread.to_json_dict() for thread in thread_map.values()]
profile_timestamp = meta_info.get('timestamp')
end_time_ms = (int(profile_timestamp) * 1000) if profile_timestamp else 0
@@ -504,6 +527,9 @@ def main() -> None:
broken-stack samples we want to remove.
"""
)
+ parser.add_argument(
+ '--percpu-samples', action='store_true',
+ help='show samples based on cpus instead of threads')
parser.add_report_lib_options()
args = parser.parse_args()
profile = _gecko_profile(
@@ -511,7 +537,9 @@ def main() -> None:
symfs_dir=args.symfs,
kallsyms_file=args.kallsyms,
report_lib_options=args.report_lib_options,
- max_remove_gap_length=args.max_remove_gap_length)
+ max_remove_gap_length=args.max_remove_gap_length,
+ percpu_samples=args.percpu_samples,
+ )
json.dump(profile, sys.stdout, sort_keys=True)