170 lines
4.4 KiB
Python
Executable File
170 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (C) 2021 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.
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import signal
|
|
import sys
|
|
import subprocess
|
|
|
|
import psutil
|
|
|
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
REGEX = re.compile(
|
|
'.*Trace loaded: ([0-9.]+) MB in ([0-9.]+)s \(([0-9.]+) MB/s\)')
|
|
|
|
|
|
def run_tp_until_ingestion(args, env):
|
|
tp_args = [os.path.join(args.out, 'trace_processor_shell'), args.trace_file]
|
|
if not args.ftrace_raw:
|
|
tp_args.append('--no-ftrace-raw')
|
|
tp = subprocess.Popen(
|
|
tp_args,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True,
|
|
env=env)
|
|
|
|
lines = []
|
|
while True:
|
|
line = tp.stderr.readline()
|
|
lines.append(line)
|
|
|
|
match = REGEX.match(line)
|
|
if match:
|
|
break
|
|
|
|
if tp.poll():
|
|
break
|
|
|
|
ret = tp.poll()
|
|
fail = ret is not None and ret > 0
|
|
if fail:
|
|
print("Failed")
|
|
for line in lines:
|
|
sys.stderr.write(line)
|
|
return tp, fail, match[2]
|
|
|
|
|
|
def heap_profile_run(args, dump_at_max: bool):
|
|
profile_args = [
|
|
os.path.join(ROOT_DIR, 'tools', 'heap_profile'), '-i', '1', '-n',
|
|
'trace_processor_shell', '--print-config'
|
|
]
|
|
if dump_at_max:
|
|
profile_args.append('--dump-at-max')
|
|
config = subprocess.check_output(
|
|
profile_args,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
|
|
out_file = os.path.join(
|
|
args.result, args.result_prefix + ('max' if dump_at_max else 'rest'))
|
|
perfetto_args = [
|
|
os.path.join(args.out, 'perfetto'), '-c', '-', '--txt', '-o', out_file
|
|
]
|
|
profile = subprocess.Popen(
|
|
perfetto_args,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL)
|
|
profile.stdin.write(config)
|
|
profile.stdin.close()
|
|
|
|
env = {
|
|
'LD_PRELOAD': os.path.join(args.out, 'libheapprofd_glibc_preload.so'),
|
|
'TRACE_PROCESSOR_NO_MMAP': '1',
|
|
'PERFETTO_HEAPPROFD_BLOCKING_INIT': '1'
|
|
}
|
|
(tp, fail, _) = run_tp_until_ingestion(args, env)
|
|
|
|
profile.send_signal(signal.SIGINT)
|
|
profile.wait()
|
|
|
|
tp.stdin.close()
|
|
tp.wait()
|
|
|
|
if fail:
|
|
os.remove(out_file)
|
|
|
|
|
|
def regular_run(args):
|
|
env = {'TRACE_PROCESSOR_NO_MMAP': '1'}
|
|
(tp, fail, time) = run_tp_until_ingestion(args, env)
|
|
|
|
p = psutil.Process(tp.pid)
|
|
mem = 0
|
|
for m in p.memory_maps():
|
|
mem += m.anonymous
|
|
|
|
tp.stdin.close()
|
|
tp.wait()
|
|
|
|
print(f'Time taken: {time}s, Memory: {mem / 1024.0 / 1024.0}MB')
|
|
|
|
|
|
def only_sort_run(args):
|
|
env = {
|
|
'TRACE_PROCESSOR_NO_MMAP': '1',
|
|
'TRACE_PROCESSOR_SORT_ONLY': '1',
|
|
}
|
|
(tp, fail, time) = run_tp_until_ingestion(args, env)
|
|
|
|
tp.stdin.close()
|
|
tp.wait()
|
|
|
|
print(f'Time taken: {time}s')
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="This script measures the running time of "
|
|
"ingesting a trace with trace processor as well as profiling "
|
|
"trace processor's memory usage with heapprofd")
|
|
parser.add_argument('--out', type=str, help='Out directory', required=True)
|
|
parser.add_argument(
|
|
'--result', type=str, help='Result directory', required=True)
|
|
parser.add_argument(
|
|
'--result-prefix', type=str, help='Result file prefix', required=True)
|
|
parser.add_argument(
|
|
'--ftrace-raw',
|
|
action='store_true',
|
|
help='Whether to ingest ftrace into raw table',
|
|
default=False)
|
|
parser.add_argument('trace_file', type=str, help='Path to trace')
|
|
args = parser.parse_args()
|
|
|
|
traced = subprocess.Popen([os.path.join(args.out, 'traced')],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL)
|
|
print('Heap profile dump at max')
|
|
heap_profile_run(args, dump_at_max=True)
|
|
print('Heap profile dump at resting')
|
|
heap_profile_run(args, dump_at_max=False)
|
|
print('Regular run')
|
|
regular_run(args)
|
|
print('Only sort run')
|
|
only_sort_run(args)
|
|
|
|
traced.send_signal(signal.SIGINT)
|
|
traced.wait()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|