Chasing down a stall (100, and code 8)¶
The timeout race decides what you see¶
Three watchdogs compete, and whichever fires first determines whether you get a
clean -100 with a sub_class, or a masked 507018:
- the scheduler no-progress timeout (
PTO2_SCHEDULER_TIMEOUT_MS), - the STARS op-execute timeout (
PTO2_OP_EXECUTE_TIMEOUT_US, ~45 s, killsaicpu-sd), - the host stream-sync timeout (
PTO2_STREAM_SYNC_TIMEOUT_MS).
A 45 s op-execute kill is not proof of a deadlock — the kernel may simply be slow. To find out which, order the race deliberately. The STs do exactly this, and the technique transfers:
- To make the scheduler win (get a
sub_class), squeeze it below the others:SCHEDULER=2000ms < OP_EXECUTE=3s < STREAM_SYNC=4000ms(aicore_hangcase). - To let a slow-but-alive path finish, push the others out of the way:
SCHEDULER=30000ms,OP_EXECUTE=30s,STREAM_SYNC=40000ms— this is how thetensor_wait_timeoutcase lets the 15 s tensor-data wait land code 8 instead of being reaped first.
These three are read once, at Worker.init(). Changing them between run()
calls on the same Worker does nothing — you must rebuild the Worker (or use a
separate process) per value. Defaults and the rationale are in
../local-timeout-defaults.md.
S1: find the stuck kernel¶
The sub_class= line gives you stuck_task_id and stuck_core. Map the task id
back to your orchestration and look at that kernel for an infinite loop, a wait on
a signal that never arrives, or simply too much work.
When the task id is not enough, lower the log threshold to DEBUG and the device log prints a task snapshot at the moment of the stall:
[STALL thread=0 idle_iterations=...] TASK ring=1 task_id=42 state=RUNNING \
fanin_refcount=0/2 kernels=[aic:3 aiv0:7 aiv1:-1] \
running_on=[owner_thread=0 cores=[core=5(AIC) core=6(AIV0)]]
kernels=[...] are the kernel ids in the task's three sub-core slots and
cores=[...] the physical cores running it — that maps "stuck task" to "which
kernel, on which core".
Setting the level:
- Worker directly:
logging.getLogger("simpler").setLevel("DEBUG")beforeworker.init()— the level is snapshotted at init and pushed to the device, so setting it betweenrun()calls has no effect. - pytest / scene test:
--log-level debug.
DEBUG is the most verbose level (DEBUG, INFO, TIMING, WARN, ERROR;
default TIMING). Device logs land
in the shared ~/ascend/log/debug/device-<id>/ by default, where several processes
interleave; redirect them per-run with ASCEND_PROCESS_LOG_PATH (the directory must
exist) before reading. See the "Device logs" section of
running-onboard.md.
Code 8, specifically¶
The tensor-data wait defaults to 15 s (PTO2_TENSOR_DATA_TIMEOUT_MS,
frequency-scaled). It means either the producer never completed, or a consumer never
released its fanout reference. Check for a hung producer first (that is S1 above),
then verify the consumer really declares the dependency and exits. If the kernel is
merely slow, raising the timeout will prove it.
If the fault line is an addressing error, not a stall¶
A core that faults stops answering, so a watchdog reaps the op afterwards and the
tail looks identical to a stall. If the device log carries a VEC/CUBE
instruction error rather than only a timeout, you are in
Chasing down an AICore fault, not here.