!7 cm > DynPrintLogAndEnabled { switchable double interval = 1.0; // seconds switchable int numberSize = 20; S aCurrentStackTrace; sclass Entry { long threadID; S threadName; WeakRef thread; StackTraceElement[] stackTrace; bool runnable; } transient L entries; transient volatile Map stackTraces; // weakHashMap transient volatile Map runnableStackTraces; start { dm_reloadOnFieldChange enabled(); if (enabled) dm_doEvery(interval, r { if (!enabled) ret; var raw = allThreadsWithStackTraces(); raw = mapMinus(raw, currentThread()); setField(entries := map(raw, (t, st) -> { new Entry e; e.threadID = threadID(t); e.threadName = threadName(t); e.thread = weakRef(t); e.stackTrace = st; e.runnable = isThreadRunnable_x(t, st); ret e; })); setField(stackTraces := toWeakHashMap(raw)); setField(runnableStackTraces := mapToWeakHashMap(filter(entries, e -> e.runnable), e -> pair(e.thread!, e.stackTrace))); setModuleName(n2(runnableStackTraces, "runnable thread") + " [Permanent Mini Profiler]"); var rand = random(runnableStackTraces); setField(aCurrentStackTrace := rand == null ?: rand.a + "\n\n" + stackTraceToString(rand.b)); vmBus_send freshStackTraces(stackTraces, weakRef(me())); }); } S renderStats() { ret stackTraces == null ? "" : jlabel_centerHTML(joinWithBR( hfontsize(numberSize, b(n2(runnableStackTraces))), (l(runnableStackTraces) == 1 ? "thread" : "threads") + " running", n2(l(stackTraces)-l(runnableStackTraces)) + " idle")); } visual westAndCenterWithMargins( dm_calculatedCenteredLabel renderStats(), dm_uneditableTextArea aCurrentStackTrace()); // API Map runnableStackTraces() { ret runnableStackTraces; } }