23#define TRACE_MAX_THREADS 12
24#define TRACE_RING_SIZE 256
25#define TRACE_DUMP_EVENTS 64
26#define TRACE_SLOW_PHASE_MS 500
27#define TRACE_STALL_MS 3000
28#define TRACE_REPEAT_MS 5000
29#define WATCHDOG_STACKSIZE (16 * 1024)
49static volatile int slotCount = 0;
51static volatile uint32_t ringPos = 0;
52static void (*stateFn)(
char *, size_t) =
nullptr;
53static Thread watchdogThread;
54static Ticks epoch = 0;
55static bool epochSet =
false;
57static Mutex & RegisterLock() {
static Mutex m;
return m; }
61static uint32_t NowMs()
66static bool IsIdle(
const char * where)
68 return !where || strcmp(where,
"idle") == 0;
71static int FindSlotIndex()
74 int count = slotCount;
76 for(
int i = 0; i < count; i++)
83static void RingAdd(
int slot,
const char * where, uint32_t now)
85 uint32_t index = __atomic_fetch_add(&ringPos, 1, __ATOMIC_RELAXED) % TRACE_RING_SIZE;
86 ring[index].when = now;
87 ring[index].slot = slot;
88 ring[index].where = where;
91void TraceThread(
const char * name)
101 if(slotCount >= TRACE_MAX_THREADS)
108 slot.
since = NowMs();
109 slotCount = slotCount + 1;
112void TraceAt(
const char * where)
114 int index = FindSlotIndex();
119 uint32_t now = NowMs();
120 const char * previous = slot.
where;
122 if(previous == where)
128 uint32_t ms = now - slot.
since;
132 RingAdd(index, where, now);
134 if(ms >= TRACE_SLOW_PHASE_MS && !IsIdle(previous))
135 LOG_WARN(
"[trace] [%s] '%s' took %u ms (now '%s')", slot.
name, previous, (
unsigned)ms, where);
138void TraceLog(
const char * fmt, ...)
143 vsnprintf(message,
sizeof(message), fmt, args);
146 int index = FindSlotIndex();
147 LOG_INFO(
"[%s] %s", index >= 0 ? slots[index].name :
"?", message);
150void TraceSetStateFn(
void (*fn)(
char *,
size_t))
155static void DumpEverything(uint32_t now)
157 int count = slotCount;
159 for(
int i = 0; i < count; i++)
160 LOG_ERROR(
"[trace] [%s] '%s' for %u ms", slots[i].name, slots[i].where ? slots[i].where :
"?", (
unsigned)(now - slots[i].since));
164 char state[192] = { 0 };
165 stateFn(state,
sizeof(state));
166 LOG_ERROR(
"[trace] state: %s", state);
170static void DumpHistory(uint32_t now)
172 uint32_t total = ringPos;
173 uint32_t events = total < TRACE_DUMP_EVENTS ? total : TRACE_DUMP_EVENTS;
175 LOG_ERROR(
"[trace] last %u phase changes, oldest first:", (
unsigned)events);
177 for(uint32_t n = events; n > 0; n--)
179 const TraceEvent &
event = ring[(total - n) % TRACE_RING_SIZE];
180 if(!event.where || event.slot < 0 || event.slot >= slotCount)
183 LOG_ERROR(
"[trace] -%5u ms [%s] %s", (
unsigned)(now - event.when), slots[event.slot].name, event.where);
187static void * watchdog(
void *)
189 uint32_t nextReport[TRACE_MAX_THREADS];
190 uint32_t reportedSince[TRACE_MAX_THREADS];
191 bool historyDumped =
false;
193 for(
int i = 0; i < TRACE_MAX_THREADS; i++)
195 nextReport[i] = TRACE_STALL_MS;
196 reportedSince[i] = 0;
199 while(!watchdogThread.stopRequested())
203 uint32_t now = NowMs();
204 int count = slotCount;
205 bool anyStalled =
false;
207 for(
int i = 0; i < count; i++)
209 uint32_t since = slots[i].since;
210 const char * where = slots[i].where;
212 if(since != reportedSince[i])
214 reportedSince[i] = since;
215 nextReport[i] = TRACE_STALL_MS;
221 uint32_t ms = now - since;
222 if(ms >= nextReport[i])
224 nextReport[i] = ms + TRACE_REPEAT_MS;
226 LOG_ERROR(
"[trace] STALL [%s] in '%s' for %u ms", slots[i].name, where, (
unsigned)ms);
236 historyDumped =
true;
244void TraceStartWatchdog()
246 watchdogThread.start(watchdog,
nullptr, WATCHDOG_STACKSIZE, ThreadPriority::Normal);
RAII lock guard - locks on construction, unlocks on destruction.
A simple mutual-exclusion lock.
static uint32_t diffMillisecs(Ticks start, Ticks end)
A lightweight, comparable identifier for a thread - including the app's own main/original thread,...
static ThreadId current()
One entry in the ring of recent phase changes.
const char *volatile where
Phase it entered.
int slot
Index into the slots array of the thread that changed phase.
One traced thread: its name and the phase it is in right now.
const char * name
Name given to TraceThread()
volatile uint32_t since
NowMs() when it entered where
ThreadId id
The traced thread.
const char *volatile where
Current phase; a string literal, compared by pointer.