How to Debug a Native Haiku Application with Debugger
Build useful symbols, capture a reproducible Haiku crash, inspect the right thread and stack, and submit evidence without leaking private data.
Haiku’s Debugger can attach to a live team or open automatically when an application crashes. The useful result is not a screenshot of an alert: it is a reproducible trigger tied to an exact binary, a symbolized stack for the relevant thread, the state of other threads and loaded images, and a privacy-reviewed report another developer can act on.
Preserve a debuggable build
Build the application from a clean revision with debug information and without stripping the binary. For a direct C++ build, add -g while retaining the warnings and optimization level you intend to investigate:
c++ -g -Wall -Wextra -std=c++17 main.cpp -o MyApp -lbe
Optimization can inline functions, reorder instructions, coalesce variables, or remove them entirely. That does not make an optimized crash invalid, but source lines and locals may look surprising. If a development build changes the failure, keep both artifacts and record their flags rather than declaring the optimized stack “wrong.”
Save the exact executable, matching source commit, resource files, dependent package versions, and architecture. A rebuilt binary with the same filename may have different addresses and cannot safely substitute for the crashed one. Do not publish proprietary or private symbols without authorization.
Reduce the failure before opening Debugger
Start from a clean application settings directory or a copied test profile only when the issue calls for it. Record the Haiku revision, architecture, package/application version, input file, initial state, numbered actions, expected outcome, and actual outcome.
Minimize one variable at a time. Determine whether the crash requires a particular document, locale, display mode, network response, add-on, or repeated action. Keep the smallest input that still triggers it, but never edit the only original evidence.
Distinguish a crash from a hang. A crash transfers control to debug_server and presents the crash alert. A hang leaves the team alive; attach while it is stuck and inspect thread states. Killing it first destroys the most useful live evidence.
Choose the correct crash-alert action
The official user guide documents four actions:
- Terminate cleans up the crashed application without further analysis.
- Debug launches Debugger attached to the stopped team.
- Save report writes a text report to the Desktop with system and crash information.
- Write core file saves a potentially very large memory image.
For an end-user report, Save report is usually the safest first artifact. Choose Debug when you can inspect the state immediately or when a developer asks for particular details. Create a core file only when requested and when secure storage/transfer is available: it may contain documents, passwords, tokens, decrypted data, message contents, and unrelated process state.
Do not repeatedly press Terminate and then try to reconstruct the crash from memory. Save at least one report before restarting the application.
Identify the faulting team and thread
In Debugger, confirm the executable path and team correspond to the program under test. Package installations, copied binaries, and development output can produce several executables with the same visible application name.
Select the thread marked as stopped by the exception or signal. Record the stop reason, instruction location, source file/line when available, and top stack frames. The first frame can be a runtime, signal, or kernel boundary; follow the stack until it reaches application or library code involved in the operation.
Copy the complete relevant backtrace, not only the final function name. Include module/image names and addresses when symbols are incomplete. A line such as “crashed in libbe” rarely distinguishes invalid caller data, an API misuse, and a Kit defect.
Read the stack critically
Walk from the oldest meaningful caller toward the fault. For each frame ask what state crossed that boundary: pointer ownership, message contents, buffer size, status code, lock, or UI object lifetime. Compare arguments and locals with invariants in the source.
Treat displayed variables cautiously. Optimized values may be unavailable or stale, and a corrupted stack can create plausible nonsense. Validate suspicious values against neighboring frames, registers, and source control flow. Do not “fix” a null pointer by adding a check until you know why the pointer was null and whether silently ignoring it preserves behavior.
Inspect the loaded-images list. Record the exact libraries/add-ons and their versions or build IDs where available. A crash in a stale private add-on is a different bug from the same stack against the repository package.
Inspect every relevant thread for a hang
For a frozen application, the visually unresponsive window thread may be waiting rather than consuming CPU. Inspect all team threads and classify their top frames: message loop, semaphore/condition wait, file or network I/O, lock acquisition, worker computation, or shutdown join.
A deadlock usually needs at least two pieces of evidence. Find the thread holding or expected to release the contested resource, and identify the reverse wait or lost wakeup. Save stack traces from all participants before changing priorities or killing a thread.
For high CPU, capture more than one stack snapshot. A single sample can land in innocent code; repeated samples at the same loop/body make the hot path more credible. Use ProcessController or ActivityMonitor to establish which team/thread consumes time, then Debugger for source-level state.
Reproduce under controlled observation
For intermittent failures, attach Debugger before the trigger when possible. Add narrow logging around state transitions, ownership, sizes, and error returns. Logs should identify event order without dumping secrets or timing-sensitive volumes of output.
Test the original binary, then a minimal candidate fix, using the same input and steps. Exercise error and cancellation paths. If the bug is a race, repeat enough times to compare rates and avoid claiming success from one non-crash.
Keep Debugger itself out of the hypothesis: attaching changes scheduling and can mask races. Preserve an unattached reproduction and, where feasible, a deterministic test that fails on the baseline.
Save and privacy-review the evidence
Before closing Debugger, save the report and copy any additional thread stacks, variables, or image details the automatic report omits. Name artifacts with application version, Haiku revision, architecture, and time.
Open the text report and review it. Remove unrelated home paths, document names, arguments, environment values, network addresses, or memory content only when they are not technically required. Mark redactions explicitly; do not rewrite stack frames. Never upload a core file to a public ticket without informed review.
An actionable ticket includes concise summary, reproducible steps, expected/actual behavior, regression range if known, hardware relevance, exact builds, report attachment, and whether symbols were present. Search existing tickets first and follow the project’s bug-tracker etiquette.
Configure automatic actions sparingly
Haiku supports ~/config/settings/system/debug_server/settings with a default_action such as user, kill, debug, log/report, or core, plus per-executable overrides. The default user prompt is appropriate for most development machines.
Automatic reporting can help unattended reproduction, but automatic core dumps can exhaust storage and retain sensitive state. Back up the settings file, scope overrides to a test executable/path, verify the produced artifact, and remove the override after the investigation.
The investigation is complete only when the evidence identifies a plausible failing boundary, the baseline reliably demonstrates it, the fix is exercised against the same trigger, and remaining uncertainty is documented. Debugger supplies state; disciplined comparison turns that state into a diagnosis.
Related:
- Fixing and Diagnosing App Crashes Using Haiku’s Debug Server
- How to Set Up a Haiku Development Environment and Build From Source
Sources: