| name | gdb-debug |
| description | Debug memory errors, segfaults, and logic bugs in nostrc GTK applications using GDB (Linux) and LLDB (macOS). Covers ASan-assisted debugging, GObject reference counting, signal handler tracing, NDB transaction analysis, and structured workflows for LLM-driven crash diagnosis.
|
| allowed-tools | Bash,Read,mcp__RepoPrompt__* |
| version | 1.0.0 |
GDB / LLDB Memory & Logic Debugging
Systematic debugger workflows for diagnosing segfaults, memory leaks, use-after-free,
and logic errors in the nostrc C/GTK4 stack. Designed for LLM agents who can run
gdb/lldb commands via Bash and interpret structured output.
Prerequisites
- Linux:
gdb installed, app built with -g (debug symbols)
- macOS:
lldb installed (ships with Xcode CLT)
- Debug build:
cmake -B build -DCMAKE_BUILD_TYPE=Debug
- Optional: ASan build:
-DGNOSTR_ENABLE_ASAN=ON -DGNOSTR_ENABLE_UBSAN=ON
Build Configurations
Debug (symbols, no optimization)
cmake -B build-debug \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS="-g3 -O0 -fno-omit-frame-pointer"
cmake --build build-debug
ASan + Debug (catches UAF, leaks, buffer overflows)
cmake -B build-asan \
-DCMAKE_BUILD_TYPE=Debug \
-DGNOSTR_ENABLE_ASAN=ON \
-DGNOSTR_ENABLE_UBSAN=ON
cmake --build build-asan
RelWithDebInfo (for profiling with symbols)
cmake -B build-rel \
-DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build-rel
GDB Workflows (Linux)
Basic crash diagnosis
gdb -ex run -ex bt -ex quit --args \
build-debug/apps/gnostr/gnostr
gdb --args build-debug/apps/gnostr/gnostr
(gdb) run
(gdb) bt full
(gdb) info threads
(gdb) thread apply all bt
Running a specific test under GDB
gdb --args build-debug/apps/gnostr/tests/gnostr-test-ndb-main-thread-violations
(gdb) run
(gdb) bt
(gdb) info locals
(gdb) print *item
GDB batch mode (for LLM agents)
Non-interactive — run commands and capture output:
gdb -batch \
-ex "set pagination off" \
-ex "set print pretty on" \
-ex "run" \
-ex "bt full" \
-ex "info threads" \
-ex "thread apply all bt" \
--args build-debug/apps/gnostr/gnostr 2>&1 | head -200
Breakpoints for common crash patterns
gdb --args build-debug/apps/gnostr/gnostr
(gdb)
(gdb) break g_log_default_handler
(gdb)
(gdb) break factory_unbind_cb
(gdb)
(gdb) break storage_ndb_begin_query
(gdb)
(gdb) break storage_ndb_begin_query if g_thread_self() == g_main_context_get_thread_default()
(gdb) run
GObject reference counting diagnosis
(gdb)
(gdb) print ((GObject*)widget)->ref_count
# Watch for ref count going to 0 (finalization)
(gdb) watch ((GObject*)0x5555DEADBEEF)->ref_count
(gdb) condition 1 ((GObject*)0x5555DEADBEEF)->ref_count == 0
# Break on GObject dispose/finalize
(gdb) break g_object_unref if object == 0x5555DEADBEEF
Signal handler investigation
(gdb)
(gdb) call g_signal_list_ids(G_OBJECT_TYPE(item), &n_ids)
(gdb)
(gdb) print g_signal_handler_is_connected(item, handler_id)
NDB transaction tracing
(gdb)
(gdb) break storage_ndb_begin_query
(gdb) commands
bt 5
continue
end
(gdb) run
Watchpoints for memory corruption
(gdb)
(gdb) watch *(int*)0x5555DEADBEEF
(gdb)
(gdb) watch self->disposed
(gdb)
(gdb) watch -l ((GObject*)self)->ref_count
LLDB Workflows (macOS)
Basic crash diagnosis
lldb -- build-debug/apps/gnostr/gnostr
(lldb) run
(lldb) bt all
(lldb) frame variable
(lldb) thread list
LLDB batch mode (for LLM agents)
lldb -b \
-o "run" \
-o "bt all" \
-o "thread list" \
-k "bt all" \
-k "quit" \
-- build-debug/apps/gnostr/gnostr 2>&1 | head -200
Breakpoints
(lldb) breakpoint set -n factory_unbind_cb
(lldb) breakpoint set -n storage_ndb_begin_query
(lldb) breakpoint set -n g_log_default_handler
(lldb) breakpoint set -n storage_ndb_begin_query -c 'gn_test_on_main_thread'
(lldb) breakpoint set -n g_logv
GObject inspection
(lldb)
(lldb) expr ((GObject*)widget)->ref_count
# Print GType name
(lldb) expr (char*)g_type_name(G_OBJECT_TYPE(item))
(lldb) expr (int)g_signal_handler_is_connected(item, handler_id)
Watchpoints
(lldb) watchpoint set expression -- ((GObject*)self)->ref_count
(lldb) watchpoint set variable self->disposed
ASan Integration
Interpreting ASan output
ASan reports look like:
=================================================================
==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x...
READ of size 8 at 0x... thread T0
#0 0x... in factory_bind_cb note-card-factory.c:245
#1 0x... in gtk_list_item_factory_bind ...
#2 0x... in gtk_list_view_activate_item ...
previously freed by thread T3:
#0 0x... in g_object_unref ...
#1 0x... in gn_nostr_event_item_finalize ...
#2 0x... in on_sync_complete sync-service.c:89
Reading the report:
- First block: Where the crash happened (accessing freed memory)
- Second block: Where the memory was freed (the root cause)
- Thread info: T0 = main thread, T3 = worker — this is a threading bug
ASan + GDB combo (Linux)
ASAN_OPTIONS=abort_on_error=1 \
gdb -ex run -ex bt --args build-asan/apps/gnostr/gnostr
ASan + LLDB combo (macOS)
ASAN_OPTIONS=abort_on_error=1 \
lldb -o run -k "bt all" -k quit \
-- build-asan/apps/gnostr/gnostr
Leak detection
ASAN_OPTIONS=detect_leaks=1 \
build-asan/apps/gnostr/tests/gnostr-test-lifecycle-leaks
Suppression files
For known leaks in third-party libraries:
cat > /tmp/lsan.supp << 'EOF'
leak:libpango
leak:libfontconfig
leak:g_type_register_static
EOF
LSAN_OPTIONS=suppressions=/tmp/lsan.supp \
build-asan/apps/gnostr/gnostr
Structured Debug Workflows for LLM Agents
Workflow 1: Diagnose a segfault
1. Reproduce:
gdb -batch -ex run -ex "bt full" -ex "thread apply all bt" \
--args build-debug/apps/gnostr/gnostr 2>&1
2. Parse the backtrace:
- Identify the crashing function and line number
- Read the source file at that line
- Check if it's a NULL deref, UAF, or logic error
3. If UAF suspected, rebuild with ASan:
cmake --build build-asan
build-asan/apps/gnostr/gnostr 2>&1
4. ASan report shows:
- WHERE it crashed (read/write of freed memory)
- WHERE it was freed (the actual bug location)
- WHICH thread freed it vs which thread accessed it
5. Fix the root cause (usually a missing g_object_ref, or
a callback accessing freed user_data)
Workflow 2: Track down a GObject leak
1. Run under Valgrind or ASan with leak detection:
ASAN_OPTIONS=detect_leaks=1 \
build-asan/apps/gnostr/tests/gnostr-test-lifecycle-leaks
2. If leak reported, check the allocation backtrace
3. In GDB, set a breakpoint on the constructor:
(gdb) break gn_nostr_event_item_new
(gdb) commands
print $rax # Return value = the new object pointer
continue
end
4. After the test, check if all objects were finalized:
- Use gn_test_watch_object() / gn_test_assert_finalized()
- Or check ref counts at test end
Workflow 3: Find main-thread blocking
1. Run with NDB violation detection:
build-debug/apps/gnostr/tests/gnostr-test-ndb-main-thread-violations
2. If violations detected, use GDB to get the full call chain:
gdb --args build-debug/apps/gnostr/tests/gnostr-test-ndb-main-thread-violations
(gdb) break gn_test_record_violation
(gdb) commands
bt 15
continue
end
(gdb) run
3. Each backtrace shows exactly how the main thread reached
the NDB transaction — trace back to find the entry point
that should be async
4. Fix: Move the call to g_task_run_in_thread() or
go_blocking_submit()
Workflow 4: Debug signal handler lifetime
1. In GDB, break on signal connect and disconnect:
(gdb) break g_signal_connect_data
(gdb) break g_signal_handler_disconnect
(gdb) # Track handler IDs being connected vs disconnected
2. Run the recycling stress test:
gdb --args build-debug/nostr-gtk/tests/nostr-gtk-test-listview-recycle
3. On crash, check:
(gdb) print handler_id # The handler that wasn't disconnected
(gdb) print item # The source object
(gdb) print row # The user_data (possibly freed)
GLib Debug Environment Variables
| Variable | Value | Purpose |
|---|
G_DEBUG | fatal-warnings | Abort on GLib warnings |
G_DEBUG | fatal-warnings,gc-friendly | + GC-friendly for leak detection |
G_SLICE | always-malloc | Disable slab allocator (needed for ASan) |
G_MESSAGES_DEBUG | all | Print all debug messages |
G_MESSAGES_DEBUG | GLib-GObject | Print only GObject messages |
GOBJECT_DEBUG | objects | Track all GObject instances |
GOBJECT_DEBUG | signals | Trace signal emissions |
GTK_DEBUG | actions | Trace GTK actions |
GTK_DEBUG | layout | Trace layout/sizing |
Combining for maximum diagnostics
G_DEBUG=fatal-warnings,gc-friendly \
G_SLICE=always-malloc \
G_MESSAGES_DEBUG=all \
GOBJECT_DEBUG=objects \
gdb --args build-debug/apps/gnostr/gnostr
Platform-Specific Notes
Linux
- GDB is the primary debugger
- ASan/LSAN fully supported
/proc/self/maps for memory mapping inspection
strace -e trace=mmap,brk for allocation tracing
- Valgrind available as alternative:
valgrind --leak-check=full ./app
macOS
Related