sclass SingleThreadProfiler is AutoCloseable { // config settable Thread threadToSample = currentThread(); settable int interval = 40; // 25 Hz settable bool skipSleeping; // results new MultiSet stackTraceMultiSet; gettable int samples; // temporary java.util.Timer timer; Lock lock = lock(); void start { lock lock; printWithMS("Starting profiler for " + threadToSample); stop(); clear(); timer = doEvery_daemon(interval, r { StackTraceElement[] stackTrace = threadToSample.getStackTrace(); if (skipSleeping && !isThreadRunnable_x(stackTrace)) ret; lock lock; samples++; stackTraceMultiSet.add(stackTraceToString(stackTrace)); }); } public void stop aka close() { lock lock; if (timer != null) { stopTimer(timer); timer = null; printWithMS("Stopped profiler for " + threadToSample); } } void clear() { lock lock; stackTraceMultiSet.clear(); samples = 0; } MultiSet results() { lock lock; ret cloneMultiSet(stackTraceMultiSet); } MultiSet stopAndGetResults() { lock lock; stop(); ret results(); } S renderFullResults(bool mostImportantLast) { var traces = results(); int n = traces.size(); int percent = ratioToIntPercent(l(traces), samples); ret (samples == 0 ? "Nothing sampled" : percent + "% core activity [" + n2(samples, "sample") + " taken]") + "\n\n" + joinMap(mostImportantLast ? traces.lowestFirst() : traces.highestFirst(), trace -> traces.get(trace) + "/" + n + "\n" + trace + "\n\n" ); } }