contrib/gfx: Adapt the game report scripts to work outside crosvm container
Performed necessary changes to the game report scripts to use them on any linux
machine:
* check for the required packages
* override the system info report with a pre-generated json file
* customize the location of temp directory
* specify a pre-captured trace file location
* other nits
BUG=b:168300956
TEST=./prepare_game_report.py \
--temp-dir /tmp/report \
--output-dir ~/ \
--trace-file ~/tf2.trace \
--sysinfo-file ~/dev_system_info.json \
440
Change-Id: I77badd40fcb25b6c5a21f12d30c1f433d0c36e3b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/2406125
Reviewed-by: Ilja H. Friedel <ihf@chromium.org>
Commit-Queue: Robert Tarasov <tutankhamen@chromium.org>
Tested-by: Robert Tarasov <tutankhamen@chromium.org>
diff --git a/contrib/gfx/prepare_game_report.py b/contrib/gfx/prepare_game_report.py
index bcc9da0..52a7039 100755
--- a/contrib/gfx/prepare_game_report.py
+++ b/contrib/gfx/prepare_game_report.py
@@ -6,6 +6,7 @@
from __future__ import print_function
+import apt
import argparse
import datetime
import hashlib
@@ -20,10 +21,11 @@
GAME_REPORT_VERSION = '2'
TRACE_STORAGE_REPORT_VERSION = '3'
TMP_DIR = '/tmp'
-GAME_TRACE_FNAME = 'game.trace'
+DEFAULT_TRACE_FNAME = 'game.trace'
TRACE_INFO_FNAME = 'trace_info.json'
GAME_INFO_FNAME = 'game_info.json'
SYSTEM_INFO_FNAME = 'system_info.json'
+REQUIRED_PACKAGES = ['apitrace', 'tar', 'zstd']
def yes_or_no(question):
@@ -63,8 +65,17 @@
f.write(json.dumps(data, indent=2))
+def parse_json_file(file_name):
+ """Parses the given JSON file and returns the result as a dictionary object"""
+ try:
+ with open(file_name) as json_file:
+ return json.loads(json_file)
+ except Exception as e:
+ return None
+
+
def parse_script_stdout_json(script, args):
- """Parses script's standart output JSON and returns as a dictionary object"""
+ """Parses script's standart output JSON and returns the result as a dictionary object"""
try:
cmd = [os.path.join(os.path.dirname(os.path.realpath(__file__)), script)
] + args
@@ -76,10 +87,45 @@
def main(args):
+ defaultTraceFileName = os.path.join(TMP_DIR, DEFAULT_TRACE_FNAME)
parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('gameid')
+ parser.add_argument('gameid', help='steam game id')
+ parser.add_argument(
+ '--temp-dir',
+ default=TMP_DIR,
+ dest='temp_dir',
+ help=f'working directory for temp files (default: {TMP_DIR})')
+ parser.add_argument(
+ '--sysinfo-file',
+ dest='sysinfo_file',
+ help='override the system info with the given json file')
+ parser.add_argument(
+ '--output-dir',
+ default=TMP_DIR,
+ dest='output_dir',
+ help=f'the output directory for the result file (default: {TMP_DIR})'
+ )
+ parser.add_argument(
+ '--trace-file',
+ default=defaultTraceFileName,
+ dest='trace_file',
+ help=f'game trace file name (default: {defaultTraceFileName})')
opts = parser.parse_args(args)
+ if opts.sysinfo_file and not os.path.exists(opts.sysinfo_file):
+ utils.panic(
+ f'Unable to open system info file {opts.sysinfo_file}. File not found.')
+
+ if opts.trace_file != defaultTraceFileName and not os.path.exists(
+ opts.trace_file):
+ utils.panic(f'Unable to open trace file {opts.trace_file}. File not found.')
+
+ # Check for missing packages
+ cache = apt.Cache()
+ for p in REQUIRED_PACKAGES:
+ if not p in cache or not cache[p].is_installed:
+ utils.panic(f'The required package "{p}" isn\'t installed.')
+
try:
cur_time = datetime.datetime.now(
datetime.timezone.utc).astimezone().replace(microsecond=0)
@@ -101,13 +147,15 @@
game_name_safe = re.sub('[^a-z0-9]', '_', game_info['game_name'].lower())
result_name = 'steam_%s-%s-%s' % (opts.gameid, game_name_safe,
cur_time.strftime('%Y%m%d_%H%M%S'))
- result_fname = f'{result_name}.tar'
+ result_file_name = f'{result_name}.tar'
+ result_full_name = os.path.join(opts.output_dir, result_file_name)
# Sanity check file/directory paths for collisions.
- if os.path.exists(result_fname):
+ if os.path.exists(result_full_name):
if yes_or_no(
- f'The file {result_fname} already exists. Do you want to delete it?'):
- os.remove(result_fname)
+ f'The file {result_full_name} already exists. Do you want to delete it?'
+ ):
+ os.remove(result_full_name)
else:
sys.exit(0)
@@ -134,38 +182,44 @@
if not game_info['can_install']:
game_info['not_enough_space'] = yes_or_no(
'Is the installation error caused by insufficient disk space?')
- save_json(game_info, os.path.join(TMP_DIR, GAME_INFO_FNAME))
+ save_json(game_info, os.path.join(opts.temp_dir, GAME_INFO_FNAME))
# Collect system/machine info report.
- system_info = {}
- system_info['host'] = {
- 'chrome': input('Paste "Google Chrome" string from chrome://version: '),
- 'platform': input('Paste "Platform" string from chrome://version: ')
- }
- print('Collecting cros container system information...')
- system_info['guest'] = parse_script_stdout_json('cros_container_info.py',
- [])
- save_json(system_info, os.path.join(TMP_DIR, SYSTEM_INFO_FNAME))
+ if opts.sysinfo_file:
+ print(f'Using system information from {opts.sysinfo_file}')
+ shutil.copyfile(opts.sysinfo_file,
+ os.path.join(opts.temp_dir, SYSTEM_INFO_FNAME))
+ else:
+ system_info = {}
+ system_info['host'] = {
+ 'chrome':
+ input('Paste "Google Chrome" string from chrome://version: '),
+ 'platform':
+ input('Paste "Platform" string from chrome://version: ')
+ }
+ print('Collecting cros container system information...')
+ system_info['guest'] = parse_script_stdout_json('cros_container_info.py',
+ [])
+ save_json(system_info, os.path.join(opts.temp_dir, SYSTEM_INFO_FNAME))
if (game_info['can_start'] and
yes_or_no('Did you manage to create the trace file?')):
- print('Preparing the trace file information for %s...' % GAME_TRACE_FNAME)
- trace_info = parse_script_stdout_json(
- 'trace_file_info.py', [os.path.join(TMP_DIR, GAME_TRACE_FNAME)])
+ print(f'Preparing the trace file information for {opts.trace_file}...')
+ trace_info = parse_script_stdout_json('trace_file_info.py',
+ [opts.trace_file])
if trace_info == None:
utils.panic('Unable to retrieve the game trace information')
file_time = datetime.datetime.fromtimestamp(
- os.path.getmtime(os.path.join(
- TMP_DIR, GAME_TRACE_FNAME))).astimezone().replace(microsecond=0)
+ os.path.getmtime(opts.trace_file)).astimezone().replace(microsecond=0)
trace_info.update({
'trace_file_name':
- GAME_TRACE_FNAME,
+ DEFAULT_TRACE_FNAME,
'trace_file_size':
- os.path.getsize(os.path.join(TMP_DIR, GAME_TRACE_FNAME)),
+ os.path.getsize(opts.trace_file),
'trace_file_time':
file_time.isoformat(),
'trace_file_md5':
- get_file_md5(os.path.join(TMP_DIR, GAME_TRACE_FNAME)),
+ get_file_md5(opts.trace_file),
'trace_can_replay':
yes_or_no('Does the trace file can be replayed without crashes?'),
})
@@ -175,34 +229,33 @@
)
trace_info['trace_replay_fps'] = input('Trace replay fps: ')
print('Compressing the trace file...')
- zstd_cmd = ['zstd', '-T0', '-f', os.path.join(TMP_DIR, GAME_TRACE_FNAME)]
+ trace_storage_file_name = DEFAULT_TRACE_FNAME + '.zst'
+ trace_storage_full_name = os.path.join(opts.temp_dir,
+ trace_storage_file_name)
+ zstd_cmd = [
+ 'zstd', '-T0', '-f', opts.trace_file, '-o', trace_storage_full_name
+ ]
subprocess.run(zstd_cmd, check=True)
- game_trace_storage_fname = GAME_TRACE_FNAME + '.zst'
trace_info.update({
- 'report_version':
- TRACE_STORAGE_REPORT_VERSION,
- 'storage_file_name':
- game_trace_storage_fname,
- 'storage_file_size':
- os.path.getsize(os.path.join(TMP_DIR, game_trace_storage_fname)),
- 'storage_file_sha256':
- get_file_sha256(os.path.join(TMP_DIR, game_trace_storage_fname)),
- 'storage_file_md5':
- get_file_md5(os.path.join(TMP_DIR, game_trace_storage_fname)),
+ 'report_version': TRACE_STORAGE_REPORT_VERSION,
+ 'storage_file_name': trace_storage_file_name,
+ 'storage_file_size': os.path.getsize(trace_storage_full_name),
+ 'storage_file_sha256': get_file_sha256(trace_storage_full_name),
+ 'storage_file_md5': get_file_md5(trace_storage_full_name),
})
- save_json(trace_info, os.path.join(TMP_DIR, TRACE_INFO_FNAME))
+ save_json(trace_info, os.path.join(opts.temp_dir, TRACE_INFO_FNAME))
items_in_archive = items_in_archive + [
- TRACE_INFO_FNAME, game_trace_storage_fname
+ TRACE_INFO_FNAME, trace_storage_file_name
]
# Finally put everything in a tarball. The --transform option is used to replace
# initial ./ prefix with {$result_name}/
- print(f'Archiving the result to {os.path.join(TMP_DIR, result_fname)}...')
+ print(f'Archiving the result to {result_full_name}...')
tar_cmd = [
- 'tar', '-cf', result_fname, '--transform', f's,^,{result_name}/,'
+ 'tar', '-cf', result_full_name, '--transform', f's,^,{result_name}/,'
] + items_in_archive
- subprocess.run(tar_cmd, check=True, cwd=TMP_DIR)
+ subprocess.run(tar_cmd, check=True, cwd=opts.temp_dir)
except Exception as e:
utils.panic(str(e))
diff --git a/contrib/gfx/trace_file_info.py b/contrib/gfx/trace_file_info.py
index 1fc512a..b6e7637 100755
--- a/contrib/gfx/trace_file_info.py
+++ b/contrib/gfx/trace_file_info.py
@@ -25,7 +25,7 @@
utils.panic('Unable to open <%s>. File not found.' % trace_fname)
try:
- cmd = ['apitrace', 'info', '--json', trace_fname]
+ cmd = ['apitrace', 'info', trace_fname]
result = subprocess.run(
cmd, check=True, encoding='utf-8', capture_output=True)