Quick answer: For native Linux builds, use signal handlers (SIGSEGV, SIGABRT) to generate minidumps using Google Breakpad or Crashpad. For Windows games running through Proton, crash dumps are generated by the Wine/Proton crash handler and stored in the Proton prefix directory.
This guide covers crash reporting for Steam Deck games in detail. The Steam Deck is a Linux device that runs most games through Proton, a Windows compatibility layer. This creates a unique challenge for crash reporting: your game thinks it is on Windows, but the underlying OS is Linux. Crash dumps work differently, file paths are translated through a virtual file system, and the device has thermal and power constraints that cause crashes you will never see on desktop hardware. Here is how to set up crash reporting that actually works on Steam Deck.
Steam Deck as a Linux Target
The Steam Deck runs SteamOS, which is an Arch Linux-based distribution. Most games run through Proton (Valve’s fork of Wine) rather than as native Linux binaries. This means your Windows game executable runs inside a compatibility layer that translates Windows API calls to Linux equivalents.
From your game’s perspective, it is running on Windows. GetVersionEx returns a Windows version. File paths look like C:\Users\steamuser\. DirectX calls work through DXVK (a Vulkan translation layer). But the actual crash dump, the system resources, and the hardware interfaces are all Linux.
This duality matters for crash reporting because:
Crash dumps are generated by the Wine crash handler, not your normal Windows crash handler. The dump format is still a Windows minidump (MDMP), but the environment in which it is created is different.
File paths in crash dumps are Wine virtual paths. A path like Z:\home\deck\.steam\steam\steamapps\common\YourGame\ corresponds to the actual Linux path /home/deck/.steam/steam/steamapps/common/YourGame/. Your symbol server and crash analysis tools need to handle this translation.
System information APIs return Proton values, not the real hardware info. To get actual Deck hardware details, you need to read from Linux sysfs directly.
Crash Dump Generation
If your game already uses a crash reporter (Breakpad, Crashpad, Sentry, or a custom handler), it likely works through Proton with some caveats. The crash handler runs inside the same Proton environment as your game, so MiniDumpWriteDump calls produce valid minidumps.
// Windows crash handler that works through Proton
LONG WINAPI CrashHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
// Create dump file path
WCHAR DumpPath[MAX_PATH];
GetTempPathW(MAX_PATH, DumpPath);
wcscat_s(DumpPath, L"game_crash.dmp");
HANDLE DumpFile = CreateFileW(DumpPath,
GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (DumpFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION DumpInfo;
DumpInfo.ThreadId = GetCurrentThreadId();
DumpInfo.ExceptionPointers = ExceptionInfo;
DumpInfo.ClientPointers = FALSE;
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
DumpFile,
MiniDumpWithDataSegs,
&DumpInfo,
NULL, NULL);
CloseHandle(DumpFile);
}
// Upload the dump asynchronously
UploadCrashDump(DumpPath);
return EXCEPTION_EXECUTE_HANDLER;
}
The dump file is written to the Proton prefix’s virtual filesystem. The actual location on disk is something like /home/deck/.steam/steam/steamapps/compatdata/[appid]/pfx/drive_c/users/steamuser/Temp/game_crash.dmp. Your upload logic needs to be able to access this file, which it can since it runs in the same Proton context.
For native Linux builds, you do not have Proton in the picture. Use signal handlers to catch crashes:
// Native Linux crash handler
#include <signal.h>
#include <execinfo.h>
void CrashSignalHandler(int Signal)
{
// Capture backtrace
void* StackFrames[64];
int FrameCount = backtrace(StackFrames, 64);
// Write to a crash log file
FILE* CrashLog = fopen("/tmp/game_crash.log", "w");
if (CrashLog)
{
fprintf(CrashLog, "Signal: %d\n", Signal);
fprintf(CrashLog, "Backtrace:\n");
backtrace_symbols_fd(StackFrames, FrameCount,
fileno(CrashLog));
fclose(CrashLog);
}
// For production, use Breakpad/Crashpad instead of
// backtrace() for proper minidump generation
_exit(1);
}
void InstallCrashHandlers()
{
signal(SIGSEGV, CrashSignalHandler);
signal(SIGABRT, CrashSignalHandler);
signal(SIGFPE, CrashSignalHandler);
signal(SIGBUS, CrashSignalHandler);
}
Symbol Upload for Proton Builds
Crash dumps are useless without matching symbols. For Proton builds, upload the same PDB files you use for Windows. The game binary is a Windows executable, so the crash stack references Windows symbols regardless of the host OS.
For native Linux builds, upload the ELF debug symbols (.debug files or the unstripped binary). Use objcopy --only-keep-debug to extract debug info from the binary without shipping it to players.
# Build pipeline: extract and upload symbols
# For Windows/Proton builds
# PDB files are generated alongside the EXE during compilation
# Upload them to your symbol server
symupload game.pdb https://symbols.example.com/upload
# For native Linux builds
# Strip the binary and keep debug symbols separate
objcopy --only-keep-debug game game.debug
strip --strip-debug game
objcopy --add-gnu-debuglink=game.debug game
# Upload the debug symbols
symupload game.debug https://symbols.example.com/upload
# Ship the stripped binary, keep debug symbols on the server
A critical mistake: uploading symbols from the wrong build. If you ship build 1.2.3 but upload symbols from build 1.2.4, every crash stack will be garbage. Always tie symbol uploads to the exact build that gets shipped.
Deck-Specific Device Context
The Steam Deck has unique hardware constraints that cause crashes not seen on desktop. Including device context in crash reports helps you distinguish Deck-specific issues from general bugs.
A crash that happens at 5% battery on a Steam Deck in handheld mode is a completely different bug than the same crash on a plugged-in desktop PC. Without device context, you will never make that connection.
Key context to collect on Steam Deck:
Device model: Steam Deck LCD vs Steam Deck OLED. They have different GPUs and thermal profiles.
Power state: Handheld vs docked. Docked mode may allow higher clock speeds and different thermal behavior.
Battery level: Low battery triggers aggressive power management that can reduce GPU and CPU clocks.
Thermal state: Read the thermal zone temperatures from sysfs. If the device is thermally throttled, GPU and CPU performance drops, which can cause timeouts and crashes in GPU-intensive code.
// Read Steam Deck specific context from Linux sysfs
// This runs on the Linux side; for Proton games,
// call through a native Linux helper or read via /proc
#ifdef __linux__
#include <fstream>
#include <string>
struct DeckDeviceContext
{
float BatteryPercent;
bool bIsCharging;
float ThermalTempCelsius;
std::string DeviceModel;
};
DeckDeviceContext ReadDeckContext()
{
DeckDeviceContext Ctx;
// Battery capacity (0-100)
std::ifstream BatCapacity("/sys/class/power_supply/BAT1/capacity");
if (BatCapacity.is_open())
{
BatCapacity >> Ctx.BatteryPercent;
}
// Charging status
std::ifstream BatStatus("/sys/class/power_supply/BAT1/status");
if (BatStatus.is_open())
{
std::string Status;
BatStatus >> Status;
Ctx.bIsCharging = (Status == "Charging");
}
// Thermal zone temperature (millidegrees Celsius)
std::ifstream Thermal("/sys/class/thermal/thermal_zone0/temp");
if (Thermal.is_open())
{
int MilliDegrees;
Thermal >> MilliDegrees;
Ctx.ThermalTempCelsius = MilliDegrees / 1000.0f;
}
// Device model from DMI
std::ifstream Product("/sys/devices/virtual/dmi/id/product_name");
if (Product.is_open())
{
std::getline(Product, Ctx.DeviceModel);
}
return Ctx;
}
#endif
Proton-Specific Crash Patterns
Some crashes are caused by Proton itself, not your game code. Recognizing these patterns saves you from debugging phantom issues:
DXVK shader compilation stutter causing timeouts. When DXVK encounters a new shader, it compiles it at runtime. On Steam Deck’s limited GPU, this compilation can cause frame time spikes that trigger timeout detection in your game or in the driver. Pre-cache shaders using Steam’s shader pre-caching system.
Wine DLL mismatch crashes. If your game bundles specific DLL versions that conflict with Proton’s built-in DLLs, crashes occur at load time. Check if the crash stack includes Wine/Proton internal DLLs like ntdll.dll or kernelbase.dll from the Proton prefix.
Memory exhaustion. The Steam Deck has 16 GB of unified RAM shared between CPU and GPU. Games that run fine on a desktop with 16 GB RAM and 8 GB VRAM may run out of memory on Deck because the same 16 GB serves both pools. Monitor memory usage and crash when approaching 14 GB total allocation.
File path length issues. The Proton prefix adds significant path length to every file path. If your game uses long file paths internally, the combined Wine virtual path + Proton prefix path may exceed MAX_PATH, causing file access failures and crashes.
Performance Context for Crash Analysis
On Steam Deck, crashes often correlate with performance conditions. A crash at a stable 60 FPS is a different kind of bug than a crash that follows 30 seconds of sub-10 FPS performance. Include performance context in your crash reports.
// Include performance context with crash reports
struct PerformanceContext
{
float AverageFrameTime; // Last 60 seconds
float WorstFrameTime; // Worst frame in last 60 seconds
int FramesBelowTarget; // Frames below 30fps in last 60s
float GPUMemoryUsedMB;
float SystemMemoryUsedMB;
float GPUTemperature;
float CPUTemperature;
bool bThermalThrottling;
};
// Maintain a rolling performance buffer
// When a crash occurs, include the last 60 seconds of data
// This shows whether the crash followed a performance degradation
This context is invaluable for diagnosing Deck-specific crashes. If every crash report shows thermal throttling and high GPU temperatures in the 60 seconds before the crash, you have a thermal-related issue that needs a quality setting adjustment, not a code fix.
Related Issues
For general crash reporting setup across engines, see our guide on automated crash reporting for indie games. If you are tracking bugs across Steam Deck and other platforms simultaneously, check tracking bugs across multiple platforms for workflow strategies.
Upload symbols for every build you ship. A crash dump without matching symbols is just a collection of random hex addresses.