tests: arm Crashpad in sgl_tests so teardown faults produce a minidump (#1062) - #1118
tests: arm Crashpad in sgl_tests so teardown faults produce a minidump (#1062)#1118nv-slang-bot[bot] wants to merge 1 commit into
Conversation
The sgl_tests C++ harness exits nonzero after a green doctest summary when a fault occurs during post-run teardown (#1062). Crashpad is compiled into the CI binary and an upload step is configured, but the handler was only armed on the Python side; the C++ main never called start_handler(), so a teardown fault wrote no minidump and the log showed only a bare 'exit 1'. Arm Crashpad after sgl::testing::static_init() and before context.run(), guarded on SGL_HAS_CRASHPAD, wrapped in try/catch so a failure to arm never takes down the run. Pass an explicit '.crashpad' database directory to match the path CI archives and the Python harness uses; the default sits next to the binary where the upload step does not look. Also fix the crashpad upload in the build-and-test-with-slang composite action (which services the cross-repo 'SlangPy Tests' lane): archive the whole .crashpad/ database instead of just reports/ (POSIX Crashpad writes to pending/, and the Python copy step is skipped when the C++ step itself crashes), and set include-hidden-files so upload-artifact does not silently skip the dot-directory. Diagnostics only: the exit-code path is untouched, so real test failures and mid-test crashes still red the job.
|
nv-slang-bot seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
|
Automated notice (PR board sync) — do not reply to this comment. Auto-assigned @jkiviluoto-nv as shepherd for this Bot PR. FYI for maintainers: committer signal on the changed files is higher for skallweitNV than for the auto-requested reviewer (jkiviluoto-nv). They were not auto-requested; a human may optionally add them as a reviewer. |
|
I'm not sure about setting the artifact path to |
|
Good question — the short answer is no, we don't lose the pytest reports; Concretely:
The reason for widening the path is the case this PR targets: when the C++ On If you'd rather keep the upload narrower, I can restrict it to Note this only touches the 🤖 Generated by an automated SlangPy coworker — may be inaccurate. A human maintainer should verify. |
Summary
-DSGL_ENABLE_CRASHPAD=ON); offline coworker review: APPROVE. Ready for maintainer review/merge consideration._Exitapproach).sgl_testsharness so the next teardown fault should produce a stackwalkable minidump (when the handler starts successfully). Does not alter the exit-code path — real test failures and mid-test crashes still red the job.crash-reports-<os>-<platform>-<compiler>-<config>artifact and stackwalk the teardown fault frame.Motivation
sgl_tests(the C++ doctest harness, run viapython tools/ci.py unit-test-cpp) intermittently exits nonzero after a green doctest summary — the fault happens during teardown, aftercontext.run()has already returned success.tools/ci.py'srun_commandthen raisesRuntimeErroron the nonzero exit, reddening the SlangPy Tests check (including on unrelated slang PRs viarepository_dispatch). Two confirmed occurrences with an identical signature (runs 27965567210, 29232873855).Crashpad is compiled into the CI binary (
ci.pysets-DSGL_ENABLE_CRASHPAD=ONfor thecrashpadmatrix rows) and an upload step is configured, but the handler was only armed on the Python side (slangpy/testing/plugin.py→crashpad.start_handler()). The C++sgl_testsmain()never calledstart_handler(), so a teardown fault wrote no minidump — the upload step produced no usable artifact and the log showed only a bareexit 1with no stack. We are blind to the exact teardown frame.Note that the cross-repo "SlangPy Tests" check that reds slang PRs runs via
repository_dispatchthrough.github/workflows/ci-latest-slang.yml→ the.github/actions/build-and-test-with-slangcomposite action, which has its own crashpad upload step; the mainci.ymluploader services the in-repo runs. This PR updates the composite action (the #1062 lane); theci.ymlcounterpart is a maintainer follow-up (the bot lacks theworkflowsApp permission to push it).Maintainer jhelferty-nv greenlit arming Crashpad for the C++ harness (issue #1062 comment 5320479090), after skallweitNV rejected the earlier
_Exit-based exit-code masking on #1064.Proposed solution
Arm Crashpad in
sgl_testsmain()immediately aftersgl::testing::static_init()and beforecontext.run(), guarded onSGL_HAS_CRASHPAD. This reuses the already-compiled-in Crashpad support, so the primary gap was the missing runtimestart_handler()call on the C++ side. A secondary gap in the upload path also had to close for the dump to actually reach an artifact: the CI uploaders archived only.crashpad/reports/(missing the POSIXpending/dump) andupload-artifactskips hidden files, so the dot-directory's contents were excluded. This PR closes both on therepository_dispatch"SlangPy Tests" lane (the #1062 target); the identicalci.ymledit for in-repo runs is a maintainer follow-up (the bot lacks theworkflowsApp permission). With the handler armed and the upload fixed, the next teardown fault should leave a stackwalkable minidump — when the handler starts successfully.This is instrumentation, not a fix: the exit-code path is untouched, so a real test failure (
result != 0) and a mid-test crash both still red the job exactly as today. It captures signal for the next occurrence; it does not fix the underlying teardown fault.Change summary
tests/sgl/sgl_tests.cpp#include "sgl/core/config.h"(defines the guard macro) and#include "sgl/utils/crashpad.h"(the API). Armsgl::crashpad::start_handler({}, ".crashpad")under#if SGL_HAS_CRASHPAD, afterstatic_init()and beforecontext.run(), wrapped in atry/catchthat logs and continues..github/actions/build-and-test-with-slang/action.yml.crashpad/reports/to the whole.crashpad/database so a POSIX teardown minidump (which lands inpending/) is archived even when the crash is in the C++ step, and setinclude-hidden-files: truesoupload-artifactdoes not skip the dot-directory's contents. This composite action services therepository_dispatch"SlangPy Tests" lane — the one that reds slang PRs, i.e. the actual #1062 target.Concepts and vocabulary
SGL_HAS_CRASHPAD— the source-visible compile define, generated intosgl/core/config.h(src/sgl/CMakeLists.txt), set ON only when the cmake optionSGL_ENABLE_CRASHPAD=ONand the crashpad package is found. This is the correct guard;SGL_ENABLE_CRASHPADis only the cmake option name and is not visible to source. It is the same guardsrc/sgl/utils/crashpad.cppuses.start_handler(handler, database, ...)writes minidumps under<database>. On Windows the.dmpfiles land directly in<database>/reports/; on POSIX they go to<database>/pending/and the Python harness copies them intoreports/.Process report
Include footgun (why the two explicit includes).
#if SGL_HAS_CRASHPADsilently evaluates to#if 0ifconfig.his not in the translation unit — the code would then compile to nothing and the instrumentation would silently do nothing. Todaysgl_tests.cppreachesconfig.honly transitively (e.g. viasgl/device/agility_sdk.handsgl/device/device.h). Rather than depend on those incidental includes surviving a future refactor, the guard's defining header is included explicitly, alongside the crashpad API header. Thesgltarget exposes the generated-config include directory asPUBLIC(src/sgl/CMakeLists.txt) andsgl_testslinkssgl, so both includes resolve.Explicit database path (why not a bare
start_handler()).start_handler()defaults its database toruntime_directory()/crashpad_database— next to the test binary. But CI archives the.crashpaddirectory relative to the repo root, and the Python harness (slangpy/testing/crashpad.py) already passesdatabase=<repo_root>/.crashpad. A barestart_handler()in C++ would write the minidump to a directory the upload step never looks at, so the whole point of the change — getting the dump archived — would be lost. Passing the explicit".crashpad"path makes the C++ harness land minidumps in the same archived location the Python side uses. (The path is relative to the process CWD;ci.pyinvokessgl_testsfrom the repo root, matching how the upload glob and the PythonPROJECT_DIR/.crashpadresolve.)Best-effort arming.
start_handler()throws (std::runtime_errorviaSGL_THROW) if the handler executable is missing or the handler fails to start. Since this is diagnostics that must never perturb the test result, the call is wrapped in atry/catchthat logs a warning and continues — mirroring the Python harness (slangpy/testing/crashpad.py), which catches and prints on failure.Archiving the POSIX dump (why the CI upload change). Both CI uploaders —
ci.ymlfor in-repo runs, and thebuild-and-test-with-slangcomposite action for therepository_dispatchlane — archived only.crashpad/reports/. On Windows (where both observed occurrences happened) Crashpad writes the.dmpdirectly into<database>/reports/, matching that old glob. On POSIX it writes to<database>/pending/, and only the Python harness's terminal-summary hook copiespending/ → reports/. But when the C++unit-test-cppstep is the one that crashes, its nonzero exit fails that step and thesuccess()-gatedunit-test-pythonstep is skipped, so the copy never runs. This PR updates the composite action (the #1062 target lane) to archive the whole.crashpad/database (the upload step isif: always()), which captures thepending/dump regardless.upload-artifact@v7excludes hidden files by default and treats everything under a dot-directory as hidden, soinclude-hidden-files: trueis required or the artifact would be silently empty (masked byif-no-files-found: ignore). The equivalentci.ymledit is a maintainer follow-up (see the note in the change summary): the bot cannot push workflow files.Invariant preserved. The teardown sequence and
return resultare unchanged; nothing about the exit code is altered. A genuine failing test still returns nonzero and reds the job; a mid-test crash always reds the job and is captured by Crashpad when the handler started successfully. The change only means that when the teardown fault next fires, a minidump should be available to stackwalk (again, when the handler started successfully).Interpreting a null result. Crashpad captures a hardware fault or
abort()(SIGSEGV/SIGABRT-class), not a graceful nonzero return. If #1062's teardown failure turns out to be a clean nonzero exit rather than a crash signal, no minidump is produced — so a "no dump uploaded" on a future red run does not necessarily mean arming failed; a null result stays inconclusive (it could be a graceful nonzero exit, a handler that failed to start, or an upload miss). A dump, on the other hand, is a positive signal that pins the fault. This is still the correct first diagnostic step: capturing a dump collapses the solution space, while its absence remains inconclusive.PR #1064 (the rejected
_Exitapproach) is superseded by this instrumentation-only approach and will be closed in its favor.🤖 Generated by an automated SlangPy coworker — may be inaccurate. A human maintainer should verify.