package se.krka.kahlua.profiler; import se.krka.kahlua.vm.*; import java.util.TimerTask; import java.util.Timer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * A sampler holds a timer task which periodically * inspects the current call frame stack of a Kahlua thread. * * For each sample taken, the sampler provides it to the profiler. */ public class Sampler { private static final AtomicInteger NEXT_ID = new AtomicInteger(); private final KahluaThread thread; private final Timer timer; private final long period; private final Profiler profiler; /** * * @param thread the Kahlua thread to measure. * @param period the number of milliseconds between each sample. * @param profiler the profiler which should receive the samples. */ public Sampler(KahluaThread thread, long period, Profiler profiler) { this.thread = thread; this.period = period; this.profiler = profiler; timer = new Timer("Kahlua Sampler-" + NEXT_ID.incrementAndGet(), true); } public void start() { TimerTask timerTask = new TimerTask() { @Override public void run() { List<StacktraceElement> list = new ArrayList<StacktraceElement>(); appendList(list, thread.currentCoroutine); profiler.getSample(new Sample(list, period)); } }; timer.scheduleAtFixedRate(timerTask, 0, period); } private void appendList(List<StacktraceElement> list, Coroutine coroutine) { while (coroutine != null) { LuaCallFrame[] stack = coroutine.getCallframeStack(); int top = Math.min(stack.length, coroutine.getCallframeTop()); for (int i = top - 1; i >= 0; i--) { LuaCallFrame frame = stack[i]; int pc = frame.pc - 1; LuaClosure closure = frame.closure; JavaFunction javaFunction = frame.javaFunction; if (closure != null) { list.add(new LuaStacktraceElement(pc, closure.prototype)); } else if (javaFunction != null) { list.add(new JavaStacktraceElement(javaFunction)); } } coroutine = coroutine.getParent(); } } public void stop() { timer.cancel(); } }