[tools][profiling] Add googler pprof support for chrome helper

- Add gcert/gcertstatus support for chrome helper
- Skip pprof uploading for non-googlers
- Print better local results instructions for multiple chromium
  results files
- Fix docs link in --help text
- Exit silently when a keyboard interrupt ocurred

Drive-by-fix:
- format files
- sort imports

Change-Id: I88bae27102dbf3d560c4203774d9746e96fdbdc5
No-Try: True
No-CQ: True
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3878166
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83069}
This commit is contained in:
Camillo 2022-09-07 09:44:12 +02:00 committed by V8 LUCI CQ
parent 94c28eb72f
commit 2c0a49f39c
2 changed files with 72 additions and 22 deletions

View File

@ -3,17 +3,21 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from datetime import datetime
from datetime import timedelta
import multiprocessing
import optparse
from pathlib import Path
from datetime import datetime, timedelta
import os
from pathlib import Path
import shlex
import subprocess
import shutil
import signal
import subprocess
import sys
import tempfile
import time
import psutil
import multiprocessing
renderer_cmd_file = Path(__file__).parent / 'linux-perf-chrome-renderer-cmd.sh'
assert renderer_cmd_file.is_file()
@ -28,7 +32,7 @@ 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.
See https://v8.dev/docs/linux-perf for more detailed instructions.
"""
parser = optparse.OptionParser(usage=usage)
parser.add_option(
@ -74,7 +78,6 @@ def log(*args):
print(*args)
print("=" * 80)
# ==============================================================================
(options, args) = parser.parse_args()
@ -135,7 +138,10 @@ with tempfile.TemporaryDirectory(prefix="chrome-") as tmp_dir_path:
if options.user_data_dir is None:
cmd.append(f"--user-data-dir={tempdir}")
cmd += [
"--no-sandbox", "--incognito", "--enable-benchmarking", "--no-first-run",
"--no-sandbox",
"--incognito",
"--enable-benchmarking",
"--no-first-run",
"--no-default-browser-check",
f"--renderer-cmd-prefix={options.renderer_cmd_prefix}",
]
@ -189,7 +195,8 @@ with tempfile.TemporaryDirectory(prefix="chrome-") as tmp_dir_path:
process.wait()
elif return_status != 0:
log("ERROR running perf record")
# ==============================================================================
# ==============================================================================
log("PARALLEL POST PROCESSING: Injecting JS symbols")
@ -219,6 +226,7 @@ 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
@ -227,6 +235,38 @@ for output_file in reversed(results):
f"{output_file.name:67}{(output_file.stat().st_size*BYTES_TO_MIB):10.2f}MiB"
)
# ==============================================================================
path_strings = [str(path.relative_to(old_cwd)) for path in results]
largest_result = path_strings[-1]
results_str = ' '.join(path_strings)
if not shutil.which('gcertstatus'):
log("ANALYSIS")
print(f"perf report --input='{largest_result}'")
print(f"pprof {path_strings}")
exit(0)
log("PPROF")
path_strings = map(lambda f: str(f.relative_to(old_cwd)), results)
print(f"pprof -flame { ' '.join(path_strings)}")
has_gcert = False
try:
print("# Checking gcert status for googlers")
subprocess.check_call("gcertstatus >&/dev/null || gcert", shell=True)
has_gcert = True
cmd = ["pprof", "-flame", f"-add_comment={shlex.join(sys.argv)}"]
print("# Processing and uploading largest pprof result")
url = subprocess.check_output(cmd + [largest_result]).decode('utf-8').strip()
print("# PPROF RESULT")
print(url)
print("# Processing and uploading combined pprof result")
url = subprocess.check_output(cmd + path_strings).decode('utf-8').strip()
print("# PPROF RESULT")
print(url)
except subprocess.CalledProcessError as e:
if has_gcert:
raise Exception("Could not generate pprof results") from e
print("# Please run `gcert` for generating pprof results")
print(f"pprof -flame {' '.join(path_strings)}")
except KeyboardInterrupt:
exit(1)

View File

@ -3,17 +3,20 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from datetime import datetime, timedelta
from pathlib import Path
from datetime import datetime
from datetime import timedelta
import optparse
import os
import psutil
from pathlib import Path
import shlex
import shutil
import signal
import subprocess
import sys
import time
import psutil
# ==============================================================================
usage = """Usage: %prog [OPTION]... $D8_BIN [D8_OPTION]... [FILE]
@ -23,8 +26,8 @@ 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.
See $D8_BIN --help for more options
See https://v8.dev/docs/linux-perf for more detailed instructions.
See $D8_BIN --help for more flags/options
"""
parser = optparse.OptionParser(usage=usage)
# Stop parsing options after D8_BIN
@ -212,7 +215,6 @@ else:
# ==============================================================================
log("POST PROCESSING: Injecting JS symbols")
def inject_v8_symbols(perf_dat_file):
output_file = perf_dat_file.with_suffix(".data.jitted")
cmd = [
@ -227,7 +229,6 @@ def inject_v8_symbols(perf_dat_file):
return None
return output_file
result = inject_v8_symbols(perf_data_file)
if result is None:
print("No perf files were successfully processed"
@ -238,18 +239,27 @@ BYTES_TO_MIB = 1 / 1024 / 1024
print(f"{result.name:67}{(result.stat().st_size*BYTES_TO_MIB):10.2f}MiB")
# ==============================================================================
if not shutil.which('gcertstatus'):
log("ANALYSIS")
print(f"perf report --input='{result}'")
print(f"pprof '{result}'")
exit(0)
log("PPROF")
has_gcert = False
try:
print("# Checking gcert status for googlers")
subprocess.check_call("gcertstatus >&/dev/null || gcert", shell=True)
has_gcert = True
cmd = ["pprof", "-flame", f"-add_comment={shlex.join(sys.argv)}", str(result)]
print("# Uploading/Processing pprof result")
print("# Processing and uploading to pprofresult")
url = subprocess.check_output(cmd).decode('utf-8').strip()
print(url)
except subprocess.CalledProcessError as e:
if not has_gcert:
print("# Please run `gcert` for generating pprof results")
else:
print(e)
if has_gcert:
raise Exception("Could not generate pprof results") from e
print("# Please run `gcert` for generating pprof results")
print(f"pprof -flame {result}")
except KeyboardInterrupt:
exit(1)