Debugging LuisaCompute
SkillMonitoring & opsDebug crashes and test failures via stack-traces, host/device logging, and DSL buffer inspection.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Debugging LuisaCompute skill
What this skill tells your AI
The instructions your AI receives, as published by luisagroup/luisacompute in .agents/skills/debug/SKILL.md and read by ahel’s review.
1. Interpreting Stack-Traces
When a crash or LUISA_ERROR is emitted, capture the full console output first.
What to look for:
- Top frames — the actual fault (null dereference, assertion, backend error).
- LuisaCompute frames — functions prefixed with
luisa::, especiallyluisa::compute::orluisa::dsl::. - Backend frames —
cuda,dx,metal,cpubackend symbols tell you which path failed. - Last log line — often the preceding
LUISA_INFO/LUISA_VERBOSEshows the dispatch or shader name that triggered the bug.
Action:
- Read the innermost frame (first after the crash header). This is the immediate cause.
- Walk upward until you hit a recognizable LuisaCompute API call (e.g.,
Device::compile,Stream::dispatch,Buffer::copy_from). That is the call-site. - If the trace ends inside a driver/shared library, suspect (a) invalid resource usage (out-of-bounds buffer/image access), or (b) backend-specific limitation.
2. Plan Before Fixing
Once the stack-trace points to a file/line or API call, write a debug plan in this order:
- Hypothesis — state what you believe caused the failure in one sentence.
- Verification — describe the smallest code change or log addition that can confirm/disprove the hypothesis.
- Fix strategy — if verified, what exactly will you change.
- Rollback marker — note the original state so you can undo cleanly.
If the fix fails:
- Save the failed attempt with memory.
- Re-read the stack-trace and the saved steps. Do not repeat a failed hypothesis.
- Pick the next most likely cause and repeat from step 1.
3. When There Is No Stack-Trace
Silent failures (hang, wrong result, test timeout) provide no trace.
Find the entry point:
- Read
CMakeLists.txtorxmake.luanear the failing target to locate the executable source file and itsmain(). - Identify the test harness (e.g.,
test_device.h,boost::ut) and how the device is created.
Add host-side logging:
#include <luisa/core/logging.h>
// In host code (C++ runtime)
LUISA_VERBOSE("Entering {}::{}", __FILE__, __func__);
LUISA_INFO("Buffer size = {}", buf.size());
LUISA_VERBOSE_WITH_LOCATION("Dispatching kernel X");
Set log level early (before Context creation if possible):
luisa::log_level_verbose(); // or log_level_info()
Progressive narrowing:
- Log at the start of
main()and at every major phase (context → device → stream → compile → dispatch). - If the failure happens during a kernel dispatch, move to device-side logging (Section 4).
- If the failure is a wrong numerical result, move to buffer read-back (Section 5).
4. DSL / Device-Side Logging
Inside kernels, use device_log to emit per-thread messages. They are collected by the stream and flushed to the host callback or default logger.
Basic usage:
#include <luisa/dsl/syntax.h>
#include <luisa/dsl/sugar.h>
Kernel2D k = [&]() noexcept {
UInt2 coord = dispatch_id().xy();
$if (coord.x == 1) {
device_log("hello {} {}", coord, make_float3x3());
};
};
Custom log callback on the stream:
Stream stream = device.create_stream();
stream.set_log_callback([](luisa::string_view message) {
LUISA_INFO("device: {}", message);
});
stream << shader().dispatch(128u, 128u) << synchronize();
Structured severity prefixes (for custom routing):
// Example pattern from test_printer_custom_callback.cpp
#define DEVICE_INFO(FMT, ...) \
device_log(luisa::format("I" FMT) __VA_OPT__(, ) __VA_ARGS__)
#define DEVICE_WARNING(FMT, ...) \
device_log(luisa::format("W" FMT) __VA_OPT__(, ) __VA_ARGS__)
#define DEVICE_ERROR(FMT, ...) \
device_log(luisa::format("E" FMT) __VA_OPT__(, ) __VA_ARGS__)
stream.set_log_callback([](luisa::string_view msg) {
if (!msg.empty()) {
switch (msg.front()) {
case 'I': luisa::log_info("{}", msg.substr(1)); break;
case 'W': luisa::log_warning("{}", msg.substr(1)); break;
case 'E': luisa::log_error("{}", msg.substr(1)); break;
default: luisa::log_verbose("{}", msg); break;
}
}
});
Important: Device logs are asynchronous. Always synchronize() the stream before assuming all logs have arrived. If a kernel hangs, the callback may never fire for logs buffered inside the failing dispatch.
5. Using Buffer for DSL Debug
When you need to inspect many values or avoid per-thread log flooding, write results into a Buffer and read back on the host.
Buffer-based inspection:
#include <luisa/core/stl/vector.h>
#include <luisa/dsl/syntax.h>
#include <luisa/dsl/sugar.h>
Buffer<float4> debug_buf = device.create_buffer<float4>(1024);
Kernel1D k = [](BufferVar<float4> out) noexcept {
UInt idx = dispatch_id().x;
Float4 v = make_float4(cast<float>(idx),
cast<float>(idx) * 2.0f,
cast<float>(idx) * 3.0f,
0.0f);
out.write(idx, v);
};
auto shader = device.compile(k);
stream << shader(debug_buf).dispatch(1024)
<< synchronize();
// Read back
luisa::vector<float4> host(1024);
stream << debug_buf.copy_to(luisa::span{host}) << synchronize();
for (size_t i = 0; i < 8; ++i) {
LUISA_INFO("host[{}] = {}", i, host[i]);
}
Reducer pattern for conditional values:
- Allocate a
Buffer<uint>counter at index 0. - In the kernel, atomically increment the counter and write the debug payload into
debug_buf[counter]. - This captures the first N interesting threads without over-allocating.
6. Environment Variables for Backend Diagnosis
| Variable | Effect |
|---|---|
LUISA_DUMP_SOURCE=1 | Dumps generated shader sources/bytecode for the active backend. |
LUISA_LOG_LEVEL=verbose | Equivalent to log_level_verbose() at startup. |
LUISA_ENABLE_VALIDATION=1 | Wraps the device in the validation layer (catches API misuse, out-of-bounds accesses, etc.). |
LUISA_OPTIX_VALIDATION=1 | Enables OptiX validation on the CUDA backend. |
Use LUISA_DUMP_SOURCE=1 when you suspect a code-generation bug (wrong instruction, missing binding, incorrect type).
Where to find the dumps:
- DirectX:
hlsl_output_<name>.hlslin the current working directory. - Vulkan user compute (XIR→SPIR-V path):
spv_code_<name>.spvasmin the current working directory. - Vulkan user compute (LLVM→SPIR-V path):
spv_code_llvm_<name>.spvasm. - Vulkan internal HLSL consumers: backend builtins/raster may dump
hlsl_output_<name>.hlsl; ordinaryDevice::compile(Function)compute shaders must not. - CUDA:
.cusource in the runtime.cachedirectory; PTX/metadata in the runtime.datadirectory. - Metal:
.metalsource in the runtime.cachedirectory. - Fallback/CPU: CPU backend also respects
LUISA_DUMP_SOURCEand may dump intermediate sources.
The runtime directories are printed by LUISA_INFO at context creation; they default to the executable directory. When running under xmake run, dumps written directly to the current working directory will appear in the project root.
7. Decision Checklist
| Symptom | First Action | Next Action |
|---|---|---|
| Crash with stack-trace | Read innermost + first Luisa frame | Hypothesize → plan → fix |
| Silent wrong result | Add LUISA_INFO at host entry points | Use buffer read-back to inspect values |
| Kernel dispatch hangs | Check synchronize() and stream callback | Add minimal device_log at start of kernel |
| Backend compilation error | Set LUISA_DUMP_SOURCE=1 | Inspect generated .spvasm or .hlsl |
| Suspected API/resource misuse | Set LUISA_ENABLE_VALIDATION=1 | Re-run and read validation messages |
| Test timeout | Read build file for target entry | Narrow phase with host logging |
8. Windows Crash Debugging with scripts/debugger.py
A lightweight Python debugger using Windows Debug API + DbgHelp.dll to launch an x64 executable, catch second-chance exceptions, and print a symbolic stack trace from PDB symbols.
Usage:
python scripts/debugger.py <path_to_exe> [pdb_search_path] [-- <args>...]
- Arguments after
--are forwarded to the target executable. - The PDB must be next to the EXE or in
pdb_search_path. - Works on Windows x64 with Python 3.x (64-bit recommended).
Example:
python scripts/debugger.py build/bin/test.exe -- --gtest_filter=MyTest
Summary
- Stack-traces → innermost frame = cause; upward walk = call-site.
- Always plan before editing;
StepMemorysaves failed attempts. - No trace → read
CMakeLists.txt/xmake.lua, addLUISA_INFO/LUISA_VERBOSE, thendevice_log. - DSL values → prefer
Bufferwrite + host read-back for bulk inspection; usedevice_logfor targeted per-thread messages. - Backend/codegen issues → set
LUISA_DUMP_SOURCE=1to inspect generated shaders andLUISA_ENABLE_VALIDATION=1to catch API/resource misuse.
Signals
- GitHub stars
- 1k
- Forks
- 108
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
debug-luisagroup- Source
- github.com/luisagroup/luisacompute