Modules/Callstack/public/StackTrace.hpp
Шурупов Илья Викторович 1ed98b9c32 Make hang reports reliable under a debugger and in input loops
A report could be left empty: sampling thread stacks signals every
thread, and a debugger such as gdb stops the whole process on that
signal before anything is written. Debugger pauses also looked like
hangs, so resuming from a breakpoint produced a false report whose
sampling froze the app again.

The summary (phase, open scopes, input events) and application files
are now written first, before any sampling. Sampling is skipped while a
debugger is attached, and the report says so. When the watchdog's own
100 ms wait takes a second or more, the whole process was paused, and
that time no longer counts towards the stalled phase.

The input loop now counts handled events per phase, so a report shows
when the UI thread kept processing a stream of events without ever
leaving input handling. The watchdog clock is injectable for tests.
2026-10-22 11:57:47 +03:00

41 lines
979 B
C++

#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace tp {
struct FrameSymbol {
std::string module;
uintptr_t moduleOffset = 0;
std::string function;
std::string file;
size_t line = 0;
};
struct ThreadSample {
int tid = 0;
std::string name;
std::string state;
std::string waitChannel;
bool answered = false;
std::vector<uintptr_t> frames;
};
class StackTrace {
public:
static constexpr size_t kMaxFrames = 64;
static size_t captureCallingThread(uintptr_t* frames, size_t capacity);
[[nodiscard]] static FrameSymbol symbolize(uintptr_t address);
[[nodiscard]] static std::string describe(uintptr_t address);
[[nodiscard]] static bool canSampleThreads();
[[nodiscard]] static bool isDebuggerAttached();
static void prepareThreadSampling();
[[nodiscard]] static std::vector<ThreadSample> sampleAllThreads(double answerTimeoutMs = 200);
[[nodiscard]] static int callingThreadId();
};
}