Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 34 additions & 17 deletions tools/crash_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,23 @@ def Begin(self):
def End(self):
print(f"==={self.status}: {self.name}===")

def Verify(self, cond, reason=None):
def Verify(self, cond, reason=None, fail=True):
if not cond:
if reason is not None:
print(f"FAILURE: {reason}")
self.status = "FAILED"
if GIVE_UP:
raise Exception("Giving up on first failure")
if fail:
if reason is not None:
print(f"FAILURE: {reason}")
self.status = "FAILED"
if GIVE_UP:
raise Exception("Giving up on first failure")
else:
print(f"WARNING: {reason}")

def Go(self):
self.Begin()
try:
self.Do()
except Exception as e:
traceback.print_exception(e)
traceback.print_exc()
self.status = "FAILED"
self.End()
return self.status == "PASSED"
Expand All @@ -73,12 +76,14 @@ def __init__(self, module, engine, tprefix, fault):

def Do(self):
vmtype = "0" if SYMBOL_ZIPS else "1"
print("Running daemon...")
print("Running Daemon...")
p = subprocess.run([self.engine,
"-noforward", # catch stupid Linux persistent sockets (if gamelogic test...)
"-homepath", self.dir,
"-set", "vm.sgame.type", vmtype,
"-set", "vm.cgame.type", vmtype,
"-set", "vm.timeout", "30", # Timeout higher than the default since we might
# test on emulators that are too slow for real use
"-set", "sv_fps", "1000",
"-set", "common.framerate.max", "0",
"-set", "client.errorPopup", "0",
Expand All @@ -95,16 +100,21 @@ def Do(self):
stderr=subprocess.PIPE, check=bool(self.tprefix))
dumps = os.listdir(PathJoin(self.dir, "crashdump"))
assert len(dumps) == 1, dumps
if BREAKPAD_DIR is None:
return
dump = PathJoin(self.dir, "crashdump", dumps[0])
sw_out = PathJoin(TEMP_DIR, self.name + "_stackwalk.log")
with open(sw_out, "a+") as sw_f:
print(f"Extracting stack trace to '{sw_out}'...")
sw_f.truncate()
subprocess.run(Virtualize([PathJoin(BREAKPAD_DIR, "src/processor/minidump_stackwalk"), dump, SYMBOL_DIR]), check=True, stdout=sw_f, stderr=subprocess.STDOUT)
sw_f.truncate(0)
subprocess.run(Virtualize([PathJoin(BREAKPAD_DIR, "src/processor/minidump_stackwalk"), dump, SYMBOL_DIR]),
check=True, stdout=sw_f, stderr=sw_f)
sw_f.seek(0)
sw = sw_f.read()
TRACE_FUNC = "InjectFaultCmd::Run"
self.Verify(TRACE_FUNC in sw, "function names not found in trace (did you build with symbols?)")
# Check both function names and filenames. On Linux it seems like only one of them works at a time??
self.Verify(srcnames := ("Command.cpp" in sw), "source file names not found in trace", fail=False)
self.Verify(funcnames := ("InjectFaultCmd::Run" in sw), "function names not found in trace", fail=False)
self.Verify(srcnames or funcnames, "neither file nor function names found in trace")

def Virtualize(cmdline):
bin, *args = cmdline
Expand Down Expand Up @@ -146,10 +156,11 @@ def Do(self):
else:
tprefix = ""
eng = module
assert os.path.isfile(PathJoin(GAME_DIR, "crash_server" + EXE))
engine = ModulePath(eng)
assert os.path.isfile(engine), engine

if not SYMBOL_ZIPS:
if BREAKPAD_DIR is not None and not SYMBOL_ZIPS:
target = ModulePath(module)
assert os.path.isfile(target), target
print(f"Symbolizing '{target}'...")
Expand All @@ -173,6 +184,7 @@ def ArgParser(usage=None):
" Otherwise, enter end-to-end mode: symbols are produced from the binaries and VM type defaults to 1 (NaCl from PWD). In this mode you will likely need to provide pak paths via --daemon-args.")
ap.add_argument("--game-dir", type=str, default=".", help="Path to Daemon (+ gamelogic) binaries")
ap.add_argument("--breakpad-dir", type=str, default=BREAKPAD_DIR, help=r"Path to Breakpad repo containing built dump_syms and stackwalk binaries. It may be a \\wsl.localhost\ path on Windows hosts in order to symbolize NaCl.")
ap.add_argument("--no-breakpad", action="store_true", help="Do not symbolize or stack-walk; only test for dump file existence")
ap.add_argument("--give-up", action="store_true", help="Stop after first test failure")
ap.add_argument("--nacl-arch", type=str, choices=["amd64", "i686", "armhf"], default="amd64") # TODO auto-detect?
ap.add_argument("module", nargs="*",
Expand All @@ -191,7 +203,7 @@ def ArgParser(usage=None):
help="Extra arguments for Daemon (e.g. -pakpath)")
pa = ap.parse_args(sys.argv[1:])
GAME_DIR = pa.game_dir
BREAKPAD_DIR = pa.breakpad_dir
BREAKPAD_DIR = None if pa.no_breakpad else pa.breakpad_dir
GIVE_UP = pa.give_up
DAEMON_USER_ARGS = pa.daemon_args
NACL_ARCH = pa.nacl_arch
Expand All @@ -207,9 +219,14 @@ def ArgParser(usage=None):

if SYMBOL_ZIPS:
print("Symbol zip(s) detected. Using release validation mode with pre-built symbols")
for z in SYMBOL_ZIPS:
with zipfile.ZipFile(PathJoin(GAME_DIR, z), 'r') as z:
z.extractall(SYMBOL_DIR)
if BREAKPAD_DIR:
for z in SYMBOL_ZIPS:
with zipfile.ZipFile(PathJoin(GAME_DIR, z), 'r') as z:
z.extractall(SYMBOL_DIR)
if sys.platform == "darwin":
assert os.path.isdir(os.path.join(GAME_DIR, "Unvanquished.app"))
DAEMON_USER_ARGS = ["-pakpath", os.path.join(GAME_DIR, "pkg")] + DAEMON_USER_ARGS
GAME_DIR = os.path.join(GAME_DIR, "Unvanquished.app/Contents/MacOS")
else:
print("No symbol zip detected. Using end-to-end Breakpad tooling test mode with dump_syms")

Expand Down
Loading