package org.kantega.revoc.registry; import java.util.concurrent.atomic.AtomicLongArray; /** * */ public final class ThreadLocalBuffer implements Runnable { private static final int MULTI_METHOD_SLOTS = 512; private static final int MULTI_METHOD_BUFFER_SIZE = MULTI_METHOD_SLOTS*6; private final long[] multiMethodVisits = new long[MULTI_METHOD_BUFFER_SIZE]; private final long[] lineVisits = new long[MULTI_METHOD_SLOTS*32]; private static final int METHOD_SLOTS = 512; private static final int METHOD_BUFFER_SIZE = METHOD_SLOTS * 3; private final long[] methodVisits = new long[METHOD_BUFFER_SIZE]; private long lastFlush; private int flushes; private long stacktop = -1; private int lineIndex; public ThreadLocalBuffer() { reset(); } public void reset() { for (int i = 0; i < MULTI_METHOD_BUFFER_SIZE; i +=6) { multiMethodVisits[i] = -1; } for (int i = 0; i < METHOD_BUFFER_SIZE; i += 3) { methodVisits[i] = -1; } } public final void visitMethod(long methodId) { int index = getMethodIndex(methodId); long[] visits = methodVisits; long slotMethod = getMethodAtIndex(index, visits); visitSlotMethod(methodId, index, slotMethod, visits); } public final int visitMultiMethod(long methodId, long visitedLines, int lines) { int index = getMultiMethodIndex(methodId); long slotMethod = getMultimethodSlot(index); return multimethodAtSlot(methodId, slotMethod, visitedLines, lines, index); } public final void visitLine(int numvisits, int lineIndex, long methodId) { flushLineVisits(numvisits, methodId, lineIndex); if(numvisits != -1) { flushLineTime(methodId, lineIndex, Registry.time); } } public final void visitLine(int cursor, int numvisits, int lineIndex, long methodId) { if (cursor != -1) { if(numvisits != 0) { lineVisits[cursor + lineIndex] += numvisits; } } else { flushLineVisits(numvisits, methodId, lineIndex); if(numvisits != -1) { flushLineTime(methodId, lineIndex, Registry.time); } } } private void visitSlotMethod(long methodId, int index, long slotMethod, long[] visits) { if(slotMethod == methodId) { recordMethodVisitAtIndex(index, visits); } else { maybeFirstMethodVisit(methodId, index, slotMethod); } } private void maybeFirstMethodVisit(long methodId, int index, long slotMethod) { if(slotMethod == -1) { recordFirstMethodVisit(methodId, index); } else { flushMethodVisits(methodId, 1, Registry.time); } } private int getMethodIndex(long methodId) { return (hashCode(methodId) % METHOD_SLOTS) * 3; } private int hashCode(long methodId) { return (((int) (methodId ^ (methodId >>> 32))) & 0x7fffffff); } private long getMethodAtIndex(int index, long[] visits) { return visits[index]; } private void recordFirstMethodVisit(long methodId, int index) { long[] visits = methodVisits; visits[index] = methodId; recordMethodVisitAtIndex(index); } private void recordMethodVisitAtIndex(int index) { recordMethodVisitAtIndex(index, methodVisits); } private int recordExistingMultiMethod(int index, long visitedLines, int lines) { long[] visits = multiMethodVisits; recordVisitedLines(index, visits, visitedLines, lines); recordMethodVisitAtIndex(index, visits); return (int) visits[index+4]; } private void recordVisitedLines(int index, long[] visits, long visitedLines, int lines) { if(visits[index+5] != visitedLines) { // TODO: Flush old visited lines long time = visits[index +2]; flushVisitedLinesTime(visits[index], visitedLines, lines, time, visits[index+5]); visits[index+5] = visitedLines; } } private void recordMethodVisitAtIndex(int index, long[] visits) { visits[index+1] ++; visits[index+2] = Registry.time; } private int recordFirstMultiMethodVisit(long methodId, int index, int lines, long visitedLines) { long[] visits = multiMethodVisits; visits[index] = methodId; recordMethodVisitAtIndex(index, visits); visits[index + 3] = lines; long lineIndex = allocateLineIndex(lines); visits[index + 4] = lineIndex; visits[index +5] = visitedLines; return (int) lineIndex; } private void flushLineVisits() { long[] visits = multiMethodVisits; //flushes++; for (int i = 0; i < visits.length; i+=6 ) { long methodId = visits[i]; if(methodId != -1) { long methodVisits = visits[i + 1]; visits[i+1] = 0; long methodTime = visits[i + 2]; long lines = visits[i + 3]; int lineIndex = (int) visits[i + 4]; long visitedLines = visits[i + 5]; flushMethodVisits(methodId, methodVisits, methodTime); for(int l = 0; l < lines; l++) { int lindex = l + lineIndex; if(lindex >= lineVisits.length) { return; } flushLineVisits(lineVisits[lindex], methodId, l); lineVisits[lindex] = 0; } flushVisitedLinesTime(methodId, visitedLines, (int) lines, methodTime, -1); visits[i+5] = 0; } } } private int allocateLineIndex(int lines) { int current = lineIndex; lineIndex = current + lines; return current; } private void flushMethodVisits() { for (int i = 0; i < METHOD_BUFFER_SIZE; i += 3) { long methodId = getMethodAtIndex(i, methodVisits); if (methodId != -1) { flushMethodVisits(methodId, methodVisits[i + 1], methodVisits[i + 2]); methodVisits[i+1] = 0; } } } private void flushMethodVisits(long methodId, long visits, long lastTime) { int cid = (int) (methodId >> 32); int mid = (int) (methodId & 0xFFFFFFFFl); Registry.methodVisits[cid].add(mid, visits); Registry.classTouches.lazySet(cid, 1); if (Registry.methodFirstLines[cid][mid] >= Registry.lineTimes[cid].length()) { return; } Registry.lineTimes[cid].set(Registry.methodFirstLines[cid][mid], lastTime); } public void flushLineVisits(long times, long methodId, int lineIndex) { if (times == 0) { return; } int cid = (int) (methodId >> 32); int mid = (int) (methodId & 0xFFFFFFFFl); AtomicLongArray classVisits = Registry.lineVisits[cid]; int firstLine = Registry.methodFirstLines[cid][mid]; if (lineIndex + firstLine >= classVisits.length()) { return; } classVisits.addAndGet(lineIndex + firstLine, times); } private void flushLineTime(long methodId, int lineIndex, long time) { int cid = (int) (methodId >> 32); int mid = (int) (methodId & 0xFFFFFFFFl); int firstLine = Registry.methodFirstLines[cid][mid]; AtomicLongArray lineTime = Registry.lineTimes[cid]; flushLineTime(lineIndex, firstLine, time, lineTime); } private void flushLineTime(int lineIndex, int firstLine, long time, AtomicLongArray lineTime) { int index = lineIndex + firstLine; if (index >= lineTime.length()) { return; } lineTime.set(index, time); } private int multimethodAtSlot(long methodId, long slotMethod, long visitedLines, int lines, int index) { if (slotMethod == methodId) { return recordExistingMultiMethod(index, visitedLines, lines); } else { return maybeFirstMultimethod(slotMethod, methodId, lines, visitedLines, index); } } private long getMultimethodSlot(int index) { return multiMethodVisits[index]; } private int maybeFirstMultimethod(long slotMethod, long methodId, int lines, long visitedLines, int index) { if(slotMethod == -1) { return recordFirstMultiMethodVisit(methodId, index, lines, visitedLines); } else { long time = Registry.time; flushMethodVisits(methodId, 1, time); flushVisitedLinesTime(methodId, visitedLines, lines, time, -1); return -1; } } private int getMultiMethodIndex(long methodId) { return (hashCode(methodId) % MULTI_METHOD_SLOTS) * 6; } private void flushVisitedLinesTime(long methodId, long visitedLines, int lines, long lastTime, long lastVisitedLines) { int cid = (int) (methodId >> 32); int mid = (int) (methodId & 0xFFFFFFFFl); int firstLine = Registry.methodFirstLines[cid][mid]; AtomicLongArray lineTime = Registry.lineTimes[cid]; for (int i = 0; i < lines; i++) { long mask = 1 << i; // lastVisitedLines == -1 means flush in any case (at popStack) if (lastVisitedLines != -1 && ((lastVisitedLines & mask) != 0 && (visitedLines & mask) == 0)) { flushLineTime(i, firstLine, lastTime, lineTime); } else if((visitedLines & mask) != 0 && lastVisitedLines == -1){ flushLineTime(i, firstLine, lastTime, lineTime); } } } public final void pushStack(long methodId) { if (isStackTop(-1)) { stacktop = methodId; } } public final void popStack(long methodId) { if (isStackTop(methodId)) { flushStackTop(); } else if (Registry.time != lastFlush) { //lastFlush = time; //flushLineVisits(); //flushMethodVisits(); } } private void flushStackTop() { lastFlush = Registry.time; flushMethodVisits(); flushLineVisits(); stacktop = -1; //System.out.println("NUmFlush: " + flushes); flushes = 0; } private boolean isStackTop(long methodId) { return stacktop == methodId; } @Override public void run() { } }