Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dfa9737
Allow arguments to session in Slurm plugin
nchaimov Jul 29, 2026
52ab52a
Make crash handler altstack optional
nchaimov Jul 29, 2026
210bba9
Send display rank with crash reports
nchaimov Jul 29, 2026
0ce5502
Add --crash-log option
nchaimov Jul 29, 2026
c1511a7
Pass crash reports to root server, write log
nchaimov Jul 29, 2026
2dc1069
Always include executable in crash key
nchaimov Jul 29, 2026
cdb16ff
Test crash logging
nchaimov Jul 29, 2026
af96d1a
Documentation for crash handler
nchaimov Jul 31, 2026
79dcdb7
Fix handling of signals sent by kill()
nchaimov Aug 21, 2026
35a35ff
Treat any return from app handler for user-sent signal as recovered.
nchaimov Aug 21, 2026
04ea751
Change crash log format to CSV
nchaimov Aug 24, 2026
8cf17f5
Log predicted path to core file in crash log
nchaimov Aug 25, 2026
2bfceb9
Clear dumpable flag to block coredump even with pipe pattern
nchaimov Sep 10, 2026
84f95ef
Handle SIG_IGN in crash handler
nchaimov Sep 10, 2026
422388a
Treat returning app handler as resolving if unable to probe
nchaimov Sep 10, 2026
e4f3e3c
Add fork child crash tests; log pid in crash log
nchaimov Sep 10, 2026
12cc753
Use both pid and rank in crash test driver
nchaimov Sep 10, 2026
f786fa1
Add additional fork crash tests
nchaimov Sep 10, 2026
ef81809
Bail on crash dedup in fork child if not reconnected
nchaimov Sep 10, 2026
9d7a1c2
Reset crash handler on first intercept after fork
nchaimov Sep 10, 2026
f6e6f2a
Reset locks upon detected fork
nchaimov Sep 15, 2026
1d4e02c
Hostname, timestamp in crash log
nchaimov Sep 21, 2026
81b9869
Poll for crash log to be written at end of session test
nchaimov Sep 21, 2026
99b8d4d
DEBUG: rerun with debug logging if session test fails
nchaimov Sep 21, 2026
c0cd571
Slurm plugin now also applies session options to steps
nchaimov Sep 21, 2026
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
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ jobs:
run: |
docker exec node-1 bash -c 'cd Spindle-build/testsuite && flux alloc --nodes=${workers} ./run_crash_tests.sh --launcher=flux --nodes=${workers} --scratch=/shared/spindle_crash_test'

- name: Run spindle-flux-ubuntu session crash test
run: |
docker exec node-1 bash -c 'cd Spindle-build/testsuite && flux alloc --nodes=${workers} ./run_crash_tests.sh --launcher=flux --nodes=${workers} --scratch=/shared/spindle_crash_test --session'

- name: Run spindle-flux-ubuntu cross-executable crash test
run: |
docker exec node-1 bash -c 'cd Spindle-build/testsuite && flux alloc --nodes=${workers} ./run_crash_tests.sh --launcher=flux --nodes=${workers} --scratch=/shared/spindle_crash_test --cross-exe'

- name: Bring spindle-flux-ubuntu down
id: flux-ubuntu-down
if: ${{ always() }}
Expand Down Expand Up @@ -366,6 +374,14 @@ jobs:
run: |
docker exec slurm-plugin-head bash -c 'cd Spindle-build/testsuite && ./run_crash_tests.sh --launcher=slurm-plugin --nodes=${workers} --scratch=/shared/spindle_crash_test'

- name: Run spindle-slurm-plugin-ubuntu session crash test
run: |
docker exec slurm-plugin-head bash -c 'cd Spindle-build/testsuite && ./run_crash_tests.sh --launcher=slurm-plugin --nodes=${workers} --scratch=/shared/spindle_crash_test --session'

- name: Run spindle-slurm-plugin-ubuntu cross-executable crash test
run: |
docker exec slurm-plugin-head bash -c 'cd Spindle-build/testsuite && ./run_crash_tests.sh --launcher=slurm-plugin --nodes=${workers} --scratch=/shared/spindle_crash_test --cross-exe'

- name: Bring spindle-slurm-plugin-ubuntu down
id: slurm-ubuntu-down
if: ${{ always() }}
Expand Down
4 changes: 4 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ Spindle can trigger scalable loading of dlopened libraries, dependent
library, executables, python modules and specified application data
files.

Spindle can also coordinate coredump creation when a large job
crashes. Rather than letting every process write a coredump, Spindle
selects one representative process per unique crash location, and can
write a log of which ranks crashed where.

Compilation:
============
Expand Down
8 changes: 8 additions & 0 deletions containers/spindle-slurm-ubuntu/testing-srun/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ ARG BASE_VERSION=latest
FROM ghcr.io/llnl/spindle-slurm-base:${BASE_VERSION}
ARG replicas=4
ENV workers=${replicas}
ENV TMPDIR=/tmp
RUN echo 'TMPDIR="/tmp"' >> /etc/environment
ENV SPINDLE_TEST_CONTAINER=1

ARG BUILD_ROOT=containers/spindle-slurm-ubuntu/testing-srun

RUN apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get -qq install -y --no-install-recommends \
gdb \
libc6-dbg

# Slurm daemons run as $SLURM_USER
ARG SLURM_USER=slurm

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,19 @@ networks:
slurm:
driver: bridge

volumes:
crash_scratch:

# Common parameters for all nodes.
x-shared-node-parameters: &shared-node-parameters
build: *shared-build-context
networks:
- slurm
volumes:
- crash_scratch:/shared
cap_add:
- SYS_NICE # Required for libnuma
- SYS_RESOURCE # Allow changing ulimit

x-healthcheck-parameters: &healthcheck-parameters
start_period: 3s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
echo "SLURM_ROLE: ${SLURM_ROLE}"

echo "Starting munged..."
sudo -u munge /usr/sbin/munged
sudo -u munge /usr/sbin/munged

if [ -d /shared ]; then
sudo chown -R "$(id -un):$(id -gn)" /shared
sudo chmod 755 /shared
fi

if [ "${SLURM_ROLE}" = "db" ]; then
echo "Starting slurmdbd..."
Expand All @@ -13,7 +18,7 @@ elif [ "${SLURM_ROLE}" = "ctl" ] ; then
sudo -u slurm /usr/sbin/slurmctld -i -Dvvv
elif [ "${SLURM_ROLE}" = "worker" ] ; then
echo "Starting slurmd..."
sudo /usr/sbin/slurmd -Dvvv
sudo bash -c 'ulimit -c unlimited; exec /usr/sbin/slurmd -Dvvv'
fi

sleep inf
12 changes: 12 additions & 0 deletions doc/spindle.1
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ By default Spindle will attempt to auto-detect the MPI implementation from the l
\fB\-d \fIyes\fR|\fIno\fR, \fR\-\-debug=\fIyes\fR|\fIno\fR
If yes, Spindle will adjust its operations so that debuggers can also attach to spindle-controlled processes. Note that there may be other factors outside of Spindle's control that may still prevent debuggers from working on Spindle-controlled processes. As of this writing, \fB\-\-debug=yes\fR will allow gdb to attach to a Spindle process, but not TotalView. This option may also cause extra overhead when starting processes. This option defaults to no.

.TP
\fB\-\-crash\-dedup=\fIyes\fR|\fIno\fR
If yes, Spindle will coordinate coredump creation when application processes crash. Processes that crash at the same location are grouped together, with one process for each unique crash site allowed to write a coredump while the others are suppressed. Default is no.

.TP
\fB\-\-crash\-altstack=\fIyes\fR|\fIno\fR
If yes, Spindle's crash handling signal handlers will run on an alternate stack. This allows crashes caused by stack overflow on the main thread to be handled and deduplicated, but this may alter application behavior. Default is no.

.TP
\fB\-\-crash\-log\fR[=\fIPATH\fR]
Write a log file listing the ranks that crashed at each crash location. This option implies \fB\-\-crash\-dedup\fR. If \fIPATH\fR is omitted, the log is written to spindle-crash-log.NUMBER in the working directory. If \fIPATH\fR is an existing directory, the log is written into that directory. Environment variables can be used in \fIPATH\fR by prefixing them with a '$' character.

.TP
\fB\-e \fIFILE\fR, \fB\-\-preload=\fIFILE\fR
Provides a text file containing white-space separated filenames. Spindle will preload the files in \fIFILE\fR onto each node before starting process execution.
Expand Down
150 changes: 150 additions & 0 deletions doc/spindle_crash_handler_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
Spindle Crash Handler
=====================

This README describes Spindle's crash handling features, which control
coredump creation and report crash locations when an application crashes.

Overview
--------

When a large parallel application hits a bug, many processes tend to
crash at the same place. If coredumps are enabled, every crashing
process writes its own coredump, which can flood a shared file system
with thousands of near-identical files.

With crash handling enabled, each crashing process reports its crash
location to Spindle. Spindle groups the reports by crash site and
selects one process per unique site to write a coredump; coredumps
from the other processes at that site are suppressed. Spindle can
also write a crash log summarizing the crash sites and which ranks
crashed at each.

Crash handling covers crashes from SIGSEGV, SIGBUS, SIGFPE, SIGILL,
and SIGABRT.

Usage
-----

Pass `--crash-dedup` to the spindle command to enable coredump
deduplication:

```
spindle --crash-dedup srun -n 512 ./my_app
```

Add `--crash-log` to also write a crash log. Since `--crash-log`
implies `--crash-dedup`, it can be used on its own:

```
spindle --crash-log=/p/lustre/me/logs srun -n 512 ./my_app
```

As with other coredumps, the usual system settings apply. Make sure
the core file size limit (`ulimit -c`) allows coredumps on the compute
nodes, or the selected process will not be able to write one.

When Spindle runs through a resource manager plugin rather than the
spindle command, pass the same options through the plugin. With the
Slurm plugin:

```
srun --spindle="--crash-log" -n 512 ./my_app
```

With a Slurm plugin session, give the options when the session starts
and they apply to every job run in that session:

```
salloc -N4 --spindle-session="--crash-log" ./my_jobs.sh
```

With the Flux plugin, use the corresponding shell options:

```
flux run -o spindle.crash-dedup -o spindle.crash-log -N4 -n512 ./my_app
```

Crash sites
-----------

A crash site identifies where the application crashed. It has two
parts: the executable, and the location of the fault. For most signals
the location is the library and offset of the faulting instruction:

```
/home/me/my_app libfoo.so.1+0x2f10
```

For SIGABRT crashes that carry a glibc abort message, such as a failed
`assert()` or a heap corruption report, the abort message is the
location:

```
/home/me/my_app abort:my_app: solver.c:88: solve: Assertion `n > 0' failed.
```

Two processes are at the same crash site only when both parts match, so
the same library fault under two different executbales counts as two crash sites
and produces two coredumps.

The crash log
-------------

The `--crash-log[=PATH]` option writes a log describing every crash in
the job. If `PATH` is omitted, the log is written to
`spindle-crash-log.$NUMBER` in the working directory, where NUMBER is
Spindle's session number. `PATH` may name the log file itself or an
existing directory to place the default filename in. Environment
variables can be used in `PATH` by prefixing them with a `$`
character.

The log is a CSV file with a header line and one line per crashing
rank:

```
rank,hostname,pid,timestamp,exe,site,exemplar,corepath
4,node12,19204,2026-09-21T14:03:22-0700,/home/me/my_app,libsolver.so.1+0x2f10,4,/p/lustre/me/run/core.my_app.19204
5,node12,19205,2026-09-21T14:03:22-0700,/home/me/my_app,libsolver.so.1+0x2f10,4,/p/lustre/me/run/core.my_app.19204
6,node13,19206,2026-09-21T14:03:23-0700,/home/me/my_app,libsolver.so.1+0x2f10,4,/p/lustre/me/run/core.my_app.19204
0,node11,19200,2026-09-21T14:03:21-0700,/home/me/my_app,abort:my_app: solver.c:88: solve: Assertion `n > 0' failed.\n,0,/p/lustre/me/run/core.my_app.19200
1,node11,19201,2026-09-21T14:03:21-0700,/home/me/my_app,abort:my_app: solver.c:88: solve: Assertion `n > 0' failed.\n,0,/p/lustre/me/run/core.my_app.19200
```

The columns are:

- `rank`: the rank that crashed.
- `hostname`: the hostname of the node on which the process crashed.
- `pid`: the process id of the process that crashed.
- `timestamp`: the time of the crash as local time in
ISO 8601 format (`YYYY-MM-DDTHH:MM:SS+ZZZZ`).
- `exe`: the path to the executable that crashed.
- `site`: the library+offset or abort message of the crash.
- `exemplar`: the rank selected to write the coredump for the
crash site.
- `corepath`: the path to the core file repesenting the given crash site.

The log is written when the job exits, or at session end when running
in session mode. If no process crashed, no log file is created.

Interaction with application signal handlers
--------------------------------------------

Applications that install their own handlers for crash signals keep
working under Spindle. When a crash signal arrives, Spindle invokes
the application's handler first. Some runtimes handle faults as part
of normal operation, such as garbage collectors that trap writes
to protected pages. If the application's handler resolves the fault,
Spindle does not handle the signal and execution continues.

Stack overflows
---------------

A crash caused by stack overflow cannot normally run a signal
handler, because the handler has no stack to run on. The
`--crash-altstack` option makes Spindle's crash handling run on an
alternate signal stack, so stack-overflow crashes on the
application's main thread can be handled and deduplicated like any
other crash. This will cause any application-registered signal handlers for
SIGSEGV, SIGBUS, SIGFPE, SIGILL, or SIGABRT to also run on the alternate
stack, which may alter application behavior. This option is off by default.

10 changes: 10 additions & 0 deletions doc/spindle_launch_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ we have to break ABI compatibility.
Spindle deduplicates the reports by crash site, selecting one
representative per unique site, permitting only those representatives
to write a coredump.
- 'OPT_CRASH_LOG' - Write a log at job end listing which ranks
crashed at which crash sites. The log path is given in the
`crash_log` field of `spindle_args_t`. Requires
`OPT_CRASH_HANDLER`.
- 'OPT_CRASH_ALTSTACK' - The crash handler registers an alternate
signal stack on the application's main thread, allowing it to
handle crashes caused by stack overflow.

- `typedef struct { ... } spindle_args_t`

Expand Down Expand Up @@ -190,6 +197,9 @@ we have to break ABI compatibility.
- `char *preloadfile` - Points to a file containing a white-space
separated list of files that should be staged onto every node in the
job before the application runs.
- `char *crash_log` - When `OPT_CRASH_LOG` is set, the path of the
crash log file. Should be an absolute path on a file system
accessible by the Spindle servers.

The FrontEnd API
----------------
Expand Down
1 change: 1 addition & 0 deletions src/client/client/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ BASE_SRCS += $(top_srcdir)/crash_handler/crash_io.c \
$(top_srcdir)/crash_handler/crash_fmt.c \
$(top_srcdir)/crash_handler/crash_handler.c \
$(top_srcdir)/crash_handler/crash_lib_offset.c \
$(top_srcdir)/crash_handler/crash_corename.c \
$(top_srcdir)/crash_handler/crash_sigchain.c

libspindlec_socket_la_SOURCES = $(BASE_SRCS)
Expand Down
7 changes: 7 additions & 0 deletions src/client/client/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ am__objects_2 = client.lo lookup.lo should_intercept.lo exec_util.lo \
$(top_builddir)/crash_handler/crash_fmt.lo \
$(top_builddir)/crash_handler/crash_handler.lo \
$(top_builddir)/crash_handler/crash_lib_offset.lo \
$(top_builddir)/crash_handler/crash_corename.lo \
$(top_builddir)/crash_handler/crash_sigchain.lo
am_libspindlec_biter_la_OBJECTS = $(am__objects_2)
libspindlec_biter_la_OBJECTS = $(am_libspindlec_biter_la_OBJECTS)
Expand Down Expand Up @@ -182,6 +183,7 @@ am__depfiles_remade = $(top_builddir)/../utils/$(DEPDIR)/getcpu.Plo \
$(top_builddir)/crash_handler/$(DEPDIR)/crash_handler.Plo \
$(top_builddir)/crash_handler/$(DEPDIR)/crash_io.Plo \
$(top_builddir)/crash_handler/$(DEPDIR)/crash_lib_offset.Plo \
$(top_builddir)/crash_handler/$(DEPDIR)/crash_corename.Plo \
$(top_builddir)/crash_handler/$(DEPDIR)/crash_sigchain.Plo \
./$(DEPDIR)/adjust_argv.Plo ./$(DEPDIR)/client.Plo \
./$(DEPDIR)/exec_util.Plo \
Expand Down Expand Up @@ -395,6 +397,7 @@ BASE_SRCS = client.c lookup.c should_intercept.c exec_util.c \
$(top_srcdir)/crash_handler/crash_fmt.c \
$(top_srcdir)/crash_handler/crash_handler.c \
$(top_srcdir)/crash_handler/crash_lib_offset.c \
$(top_srcdir)/crash_handler/crash_corename.c \
$(top_srcdir)/crash_handler/crash_sigchain.c
libspindlec_socket_la_SOURCES = $(BASE_SRCS)
libspindlec_socket_la_LIBADD = $(top_builddir)/client_comlib/libclient_socket.la $(top_builddir)/logging/libspindleclogc.la $(top_builddir)/shm_cache/libshmcache.la
Expand Down Expand Up @@ -484,6 +487,9 @@ $(top_builddir)/crash_handler/crash_handler.lo: \
$(top_builddir)/crash_handler/crash_lib_offset.lo: \
$(top_builddir)/crash_handler/$(am__dirstamp) \
$(top_builddir)/crash_handler/$(DEPDIR)/$(am__dirstamp)
$(top_builddir)/crash_handler/crash_corename.lo: \
$(top_builddir)/crash_handler/$(am__dirstamp) \
$(top_builddir)/crash_handler/$(DEPDIR)/$(am__dirstamp)
$(top_builddir)/crash_handler/crash_sigchain.lo: \
$(top_builddir)/crash_handler/$(am__dirstamp) \
$(top_builddir)/crash_handler/$(DEPDIR)/$(am__dirstamp)
Expand Down Expand Up @@ -514,6 +520,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/crash_handler/$(DEPDIR)/crash_handler.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/crash_handler/$(DEPDIR)/crash_io.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/crash_handler/$(DEPDIR)/crash_lib_offset.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/crash_handler/$(DEPDIR)/crash_corename.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@$(top_builddir)/crash_handler/$(DEPDIR)/crash_sigchain.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adjust_argv.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Plo@am__quote@ # am--include-marker
Expand Down
Loading
Loading