Callstack: in-process stack capture and thread sampling #5

Merged
ilusha merged 2 commits from Callstack into master 2026-10-27 23:10:19 +03:00
Collaborator

Base: master @ d00b249b. Two commits, 3 files, +334/−91. Both commits are authored by agent.

Why

Hang reports need stacks from every thread, but the old CallStackCapture only captured the calling thread and resolved frames through an addr2line subprocess. That is slow, cannot run inside the signal handler used to sample a stalled process, and had no Android implementation. StackTrace puts capture, symbolization and sampling in one in-process primitive, and CallStackCapture is rebuilt on top of it.

How it works

  • captureCallingThread() uses _Unwind_Backtrace to write up to 64 return addresses into a caller-provided buffer.
  • symbolize() uses dladdr and __cxa_demangle; describe() formats address, module, module-relative offset and demangled function.
  • sampleAllThreads() enumerates /proc/self/task, skips the sampler and signals each target with SIGRTMIN+3 via tgkill. Each target captures its own stack into a per-thread slot. The sampler waits up to answerTimeoutMs (200 ms by default), then returns each thread's name, scheduler state, kernel wait channel, answered flag and frames. Slots hold at most 256 threads and the signal handler is installed once.
  • isDebuggerAttached() reads TracerPid on Linux or calls IsDebuggerPresent() on Windows, so hang reporting can avoid sampling while a debugger has the process stopped.
  • Windows uses CaptureStackBackTrace and module lookup, but canSampleThreads() is false and sampleAllThreads() returns empty.

CallStackCapture now delegates to this implementation on Linux and Android, replacing execinfo/backtrace_symbols and the addr2line subprocess.

Behavior change: symbolization reports module + offset + demangled function. Unlike the old addr2line path, it does not provide a source file/line because dladdr does not supply them.

Verification

Both sources compile with -Wall -Wextra. A local smoke test captured the calling stack and sampled a sleeping worker:

captured=5
samples=1 answered=1 debugger=0

CMakeLists.txt is unchanged: the port's MODULES_BUILD_TESTS gate does not exist on master and would silently disable the tests.

**Base:** `master` @ `d00b249b`. Two commits, 3 files, +334/−91. Both commits are authored by `agent`. ## Why Hang reports need stacks from every thread, but the old `CallStackCapture` only captured the calling thread and resolved frames through an `addr2line` subprocess. That is slow, cannot run inside the signal handler used to sample a stalled process, and had no Android implementation. `StackTrace` puts capture, symbolization and sampling in one in-process primitive, and `CallStackCapture` is rebuilt on top of it. ## How it works - `captureCallingThread()` uses `_Unwind_Backtrace` to write up to 64 return addresses into a caller-provided buffer. - `symbolize()` uses `dladdr` and `__cxa_demangle`; `describe()` formats address, module, module-relative offset and demangled function. - `sampleAllThreads()` enumerates `/proc/self/task`, skips the sampler and signals each target with `SIGRTMIN+3` via `tgkill`. Each target captures its own stack into a per-thread slot. The sampler waits up to `answerTimeoutMs` (200 ms by default), then returns each thread's name, scheduler state, kernel wait channel, `answered` flag and frames. Slots hold at most 256 threads and the signal handler is installed once. - `isDebuggerAttached()` reads `TracerPid` on Linux or calls `IsDebuggerPresent()` on Windows, so hang reporting can avoid sampling while a debugger has the process stopped. - Windows uses `CaptureStackBackTrace` and module lookup, but `canSampleThreads()` is false and `sampleAllThreads()` returns empty. `CallStackCapture` now delegates to this implementation on Linux **and Android**, replacing `execinfo`/`backtrace_symbols` and the `addr2line` subprocess. **Behavior change:** symbolization reports module + offset + demangled function. Unlike the old `addr2line` path, it does not provide a source file/line because `dladdr` does not supply them. ## Verification Both sources compile with `-Wall -Wextra`. A local smoke test captured the calling stack and sampled a sleeping worker: ``` captured=5 samples=1 answered=1 debugger=0 ``` `CMakeLists.txt` is unchanged: the port's `MODULES_BUILD_TESTS` gate does not exist on `master` and would silently disable the tests.
agent added 2 commits 2026-10-27 23:00:14 +03:00
StackTrace captures the calling thread with _Unwind_Backtrace, symbolizes
addresses with dladdr and __cxa_demangle, and samples other threads through a
real-time signal. Each target writes into its own slot; the sampler also reads
/proc task name, scheduler state and wait channel, and keeps unanswered threads
in the result. Windows captures stacks and module offsets but reports that
remote sampling is unsupported. Debugger detection is exposed separately.
Callstack: build CallStackCapture on StackTrace
Some checks are pending
Windows / build (pull_request) Waiting to run
CMake on a single platform / build (pull_request) Waiting to run
CMake / build (pull_request) Waiting to run
2e7213988d
CallStackCapture now delegates capture and symbolization to StackTrace instead
of execinfo plus an addr2line subprocess. The same implementation is enabled on
Linux and Android. Reports contain the module, module-relative offset and
demangled function; dladdr supplies no source line.
requested review from ilusha 2026-10-27 23:00:15 +03:00
agent force-pushed Callstack from 2e7213988d to e05fb9c714 2026-10-27 23:01:07 +03:00 Compare
ilusha merged commit ab5e4bcd44 into master 2026-10-27 23:10:19 +03:00
ilusha deleted branch Callstack 2026-10-27 23:10:19 +03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: ilusha/Modules#5
No description provided.