[tools] Add linux-perf helper scripts for chrome
Change-Id: I3e3a59172a0ffa482a9a3d0c23f616bbf1cf7fb5 No-Try: True Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3578858 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Commit-Queue: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/main@{#79927}
This commit is contained in:
parent
fba83fd6da
commit
d9268ee88c
45
tools/chrome/linux-perf-renderer-cmd.sh
Executable file
45
tools/chrome/linux-perf-renderer-cmd.sh
Executable file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 the V8 project authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
PERF_DATA_DIR="."
|
||||
PERF_DATA_PREFIX="chrome_renderer"
|
||||
RENDERER_ID="0"
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
--help)
|
||||
echo "Usage: path/to/chrome --renderer-cmd-prefix='$0 [OPTION]' [CHROME OPTIONS]"
|
||||
echo "This script is mostly used in conjuction with linux_perf.py to run linux-perf"
|
||||
echo "for each renderer process."
|
||||
echo "It generates perf.data files that can be read by pprof or linux-perf."
|
||||
echo ""
|
||||
echo 'File naming: ${OUT_DIR}/${PREFIX}_${PARENT_PID}_${RENDERER_ID}.perf.data'
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --perf-data-dir=OUT_DIR Change the location where perf.data is written."
|
||||
echo " Default: '$PERF_DATA_DIR'"
|
||||
echo " --perf-data-prefix=PREFIX Set a custom prefex for all generated perf.data files."
|
||||
echo " Default: '$PERF_DATA_PREFIX'"
|
||||
exit
|
||||
;;
|
||||
--perf-data-dir=*)
|
||||
PERF_DATA_DIR="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--perf-data-prefix=*)
|
||||
PERF_DATA_PREFIX="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--renderer-client-id=*)
|
||||
# Don't shift this option since it is passed in (and used by) chrome.
|
||||
RENDERER_ID="${i#*=}"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
PERF_OUTPUT="$PERF_DATA_DIR/${PERF_DATA_PREFIX}_${PPID}_${RENDERER_ID}.perf.data"
|
||||
perf record --call-graph=fp --clockid=mono --freq=max --output="${PERF_OUTPUT}" -- $@
|
207
tools/chrome/linux_perf.py
Executable file
207
tools/chrome/linux_perf.py
Executable file
@ -0,0 +1,207 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2022 the V8 project authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import optparse
|
||||
from pathlib import Path
|
||||
from re import A
|
||||
import os
|
||||
import shlex
|
||||
from signal import SIGQUIT
|
||||
import subprocess
|
||||
import signal
|
||||
import tempfile
|
||||
import time
|
||||
import psutil
|
||||
import multiprocessing
|
||||
|
||||
from unittest import result
|
||||
|
||||
renderer_cmd_file = Path(__file__).parent / 'linux-perf-renderer-cmd.sh'
|
||||
assert renderer_cmd_file.is_file()
|
||||
renderer_cmd_prefix = f"{renderer_cmd_file} --perf-data-prefix=chrome_renderer"
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
usage = """Usage: %prog $CHROME_BIN [OPTION]... -- [CHROME_OPTION]... [URL]
|
||||
|
||||
This script runs linux-perf on all render process with custom V8 logging to get
|
||||
support to resolve JS function names.
|
||||
|
||||
The perf data is written to OUT_DIR separate by renderer process.
|
||||
|
||||
See http://v8.dev//linux-perf for more detailed instructions.
|
||||
"""
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option(
|
||||
'--perf-data-dir',
|
||||
default=None,
|
||||
metavar="OUT_DIR",
|
||||
help="Output directory for linux perf profile files")
|
||||
parser.add_option(
|
||||
"--profile-browser-process",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Also start linux-perf for the browser process. "
|
||||
"By default only renderer processes are sampled. "
|
||||
"Outputs 'browser_*.perf.data' in the CDW")
|
||||
parser.add_option("--timeout", type=int, help="Stop chrome after N seconds")
|
||||
|
||||
chrome_options = optparse.OptionGroup(
|
||||
parser, "Chrome-forwarded Options",
|
||||
"These convenience for a better script experience that are forward directly"
|
||||
"to chrome. Any other chrome option can be passed after the '--' arguments"
|
||||
"separator.")
|
||||
chrome_options.add_option("--user-data-dir", dest="user_data_dir", default=None)
|
||||
chrome_options.add_option("--js-flags", dest="js_flags")
|
||||
chrome_options.add_option(
|
||||
"--renderer-cmd-prefix",
|
||||
default=None,
|
||||
help=f"Set command prefix, used for each new chrome renderer process."
|
||||
"Default: {renderer_cmd_prefix}")
|
||||
FEATURES_DOC = "See chrome's base/feature_list.h source file for more dertails"
|
||||
chrome_options.add_option(
|
||||
"--enable-features",
|
||||
help="Comma-separated list of enabled chrome features. " + FEATURES_DOC)
|
||||
chrome_options.add_option(
|
||||
"--disable-features",
|
||||
help="Command-separated list of disabled chrome features. " + FEATURES_DOC)
|
||||
parser.add_option_group(chrome_options)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
def log(*args):
|
||||
print("")
|
||||
print("=" * 80)
|
||||
print(*args)
|
||||
print("=" * 80)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) == 0:
|
||||
parser.error("No chrome binary provided")
|
||||
|
||||
chrome_bin = Path(args.pop(0))
|
||||
if not chrome_bin.exists():
|
||||
parser.error(f"Chrome '{chrome_bin}' does not exist")
|
||||
|
||||
if options.renderer_cmd_prefix is not None:
|
||||
if options.perf_data_dir is not None:
|
||||
parser.error("Cannot specify --perf-data-dir "
|
||||
"if a custom --renderer-cmd-prefix is provided")
|
||||
else:
|
||||
options.renderer_cmd_prefix = str(renderer_cmd_file)
|
||||
|
||||
if options.perf_data_dir is None:
|
||||
options.perf_data_dir = Path.cwd()
|
||||
else:
|
||||
options.perf_data_dir = Path(options.perf_data_dir).absolute()
|
||||
|
||||
if not options.perf_data_dir.is_dir():
|
||||
parser.error(f"--perf-data-dir={options.perf_data_dir} "
|
||||
"is not an directory or does not exist.")
|
||||
|
||||
if options.timeout and options.timeout < 2:
|
||||
parser.error("--timeout should be more than 2 seconds")
|
||||
|
||||
# ==============================================================================
|
||||
old_cwd = Path.cwd()
|
||||
os.chdir(options.perf_data_dir)
|
||||
|
||||
# ==============================================================================
|
||||
JS_FLAGS_PERF = ("--perf-prof --no-write-protect-code-memory "
|
||||
"--interpreted-frames-native-stack")
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="chrome-") as tmp_dir_path:
|
||||
tempdir = Path(tmp_dir_path)
|
||||
cmd = [
|
||||
str(chrome_bin),
|
||||
]
|
||||
if options.user_data_dir is None:
|
||||
cmd.append(f"--user-data-dir={tempdir}")
|
||||
cmd += [
|
||||
"--no-sandbox", "--incognito", "--enable-benchmarking", "--no-first-run",
|
||||
"--no-default-browser-check",
|
||||
f"--renderer-cmd-prefix={options.renderer_cmd_prefix}",
|
||||
f"--js-flags={JS_FLAGS_PERF}"
|
||||
]
|
||||
if options.js_flags:
|
||||
cmd += [f"--js-flags={options.js_flags}"]
|
||||
if options.enable_features:
|
||||
cmd += [f"--enable-features={options.enable_features}"]
|
||||
if options.disable_features:
|
||||
cmd += [f"--disable-features={options.disable_features}"]
|
||||
cmd += args
|
||||
log("CHROME CMD: ", shlex.join(cmd))
|
||||
|
||||
if options.profile_browser_process:
|
||||
perf_data_file = f"{tempdir.name}_browser.perf.data"
|
||||
perf_cmd = [
|
||||
"perf", "record", "--call-graph=fp", "--freq=max", "--clockid=mono",
|
||||
f"--output={perf_data_file}", "--"
|
||||
]
|
||||
cmd = perf_cmd + cmd
|
||||
log("LINUX PERF CMD: ", shlex.join(cmd))
|
||||
|
||||
if options.timeout is None:
|
||||
subprocess.run(cmd)
|
||||
else:
|
||||
process = subprocess.Popen(cmd)
|
||||
time.sleep(options.timeout)
|
||||
log(f"QUITING chrome child processes after {options.timeout}s timeout")
|
||||
current_process = psutil.Process()
|
||||
children = current_process.children(recursive=True)
|
||||
for child in children:
|
||||
if "chrome" in child.name() or "content_shell" in child.name():
|
||||
print(f" quitting PID={child.pid}")
|
||||
child.send_signal(signal.SIGQUIT)
|
||||
# Wait for linux-perf to write out files
|
||||
time.sleep(1)
|
||||
process.send_signal(signal.SIGQUIT)
|
||||
process.wait()
|
||||
|
||||
# ==============================================================================
|
||||
log("PARALLEL POST PROCESSING: Injecting JS symbols")
|
||||
|
||||
|
||||
def inject_v8_symbols(perf_dat_file):
|
||||
output_file = perf_dat_file.with_suffix(".data.jitted")
|
||||
cmd = [
|
||||
"perf", "inject", "--jit", f"--input={perf_dat_file}",
|
||||
f"--output={output_file}"
|
||||
]
|
||||
try:
|
||||
subprocess.run(cmd)
|
||||
print(f"Processed: {output_file}")
|
||||
except:
|
||||
print(shlex.join(cmd))
|
||||
return None
|
||||
return output_file
|
||||
|
||||
|
||||
results = []
|
||||
with multiprocessing.Pool() as pool:
|
||||
results = list(
|
||||
pool.imap_unordered(inject_v8_symbols,
|
||||
options.perf_data_dir.glob("*perf.data")))
|
||||
|
||||
results = list(filter(lambda x: x is not None, results))
|
||||
if len(results) == 0:
|
||||
print("No perf files were successfully processed"
|
||||
" Check for errors or partial results in '{options.perf_data_dir}'")
|
||||
exit(1)
|
||||
log(f"RESULTS in '{options.perf_data_dir}'")
|
||||
results.sort(key=lambda x: x.stat().st_size)
|
||||
BYTES_TO_MIB = 1 / 1024 / 1024
|
||||
for output_file in reversed(results):
|
||||
print(
|
||||
f"{output_file.name:67}{(output_file.stat().st_size*BYTES_TO_MIB):10.2f}MiB"
|
||||
)
|
||||
|
||||
log("PPROF EXAMPLE")
|
||||
path_strings = map(lambda f: str(f.relative_to(old_cwd)), results)
|
||||
print(f"pprof -flame { ' '.join(path_strings)}")
|
Loading…
Reference in New Issue
Block a user