package fr.inria.diversify.sosie.logger; import java.io.*; import java.util.*; import java.util.concurrent.Semaphore; /** * Abstract classes for all loggers * <p/> * Created by marodrig on 25/06/2014. */ public abstract class InstruLogWriter { //Thread containing the test private final Thread thread; private final InstruLogWriter parent; //Dir where the logs are going to be stored private String logDir = "LogDirName"; ///The call deep is how deep in the stack are we. protected Map<Thread, Integer> callDeep; ///Directory where the log is being stored protected File dir = null; ///Dictionary indicating if the methods of a thread are to be logged. protected Map<Thread, Boolean> logMethod; ///Semaphores for locking output streams protected Map<String, Semaphore> semaphores; ///Previous logs of variables status. Useful to validate whether they have change protected Map<Thread, Map<String, String>> previousVarLog; protected Boolean partialLogging = null; protected Set<Thread> partialLoggingThread; //Number of times a TP is called private HashMap<String, Integer> transplantPointCallCount; //Instrumented depths of the call private HashMap<String, Integer[]> depthInstruArray; //Number of times an assertion is called private HashMap<String, Integer> assertCallCount; public int getCallDeep(Thread t) { return callDeep.containsKey(t) ? callDeep.get(t) : 0; } //Signature of the last test method logged private String currentTestSignature; /** * Constructor for the logger * * @param logDir Directory where the logging is going to be stored */ public InstruLogWriter(String logDir, InstruLogWriter parent) { if (dir == null) initDir(logDir); semaphores = new HashMap<String, Semaphore>(); callDeep = new HashMap<Thread, Integer>(); logMethod = new HashMap<Thread, Boolean>(); setTransplantPointCallCount(parent.getTransplantPointCallCount()); setAssertCallCount(parent.getAssertCallCount()); this.parent = parent; thread = parent.getThread(); ShutdownHookLog shutdownHook = new ShutdownHookLog(); Runtime.getRuntime().addShutdownHook(shutdownHook); } /** * Constructor for the logger * * @param logDir Directory where the logging is going to be stored */ public InstruLogWriter(String logDir) { if (dir == null) initDir(logDir); semaphores = new HashMap<String, Semaphore>(); callDeep = new HashMap<Thread, Integer>(); logMethod = new HashMap<Thread, Boolean>(); transplantPointCallCount = new HashMap<String, Integer>(); assertCallCount = new HashMap<String, Integer>(); ShutdownHookLog shutdownHook = new ShutdownHookLog(); Runtime.getRuntime().addShutdownHook(shutdownHook); parent = null; thread = Thread.currentThread(); } /** * Gets the loggin path for the current thread * * @param thread Thread to log * @return The path with the log file */ public String getThreadLogFilePath(Thread thread) { return dir.getAbsolutePath() + "/" + getThreadFileName(thread); } /** * Log a call to a method * * @param thread Thread where the call is invoked * @param methodSignatureId Signature of the method */ public abstract void methodCall(Thread thread, String methodSignatureId); /** * Method that logs and return from a method * * @param thread Thread where the method returns */ public void methodOut(Thread thread) { decCallDepth(thread); } public abstract void writeAssert(int id, Thread thread, String className, String methodSignature, String assertName, Object... var); /** * Logs the beginning of a test method * * @param thread * @param testSignature */ public void writeTestStart(Thread thread, String testSignature) { setCurrentTestSignature(testSignature); getTransplantPointCallCount().clear(); getAssertCallCount().clear(); if (depthInstruArray == null) depthInstruArray = new HashMap<String, Integer[]>(); else depthInstruArray.clear(); } private void incCallCount(HashMap<String, Integer> map, String id) { if (!map.containsKey(id)) { map.put(id, 1); } else { int k = map.get(id) + 1; map.put(id, k); } } /** * Count the call of a transplant point. * * @param id */ public void writeSourcePositionCall(String id) { if (!getTransplantPointCallCount().containsKey(id)) { writeStartLogging(Thread.currentThread(), id); } incCallCount(getTransplantPointCallCount(), id); } /** * Counts an assert. * <p/> * Counts how many asserts per transformation line */ public void countAssert(String id) { incCallCount(getAssertCallCount(), id); } public abstract void writeVar(int id, Thread thread, String methodSignatureId, Object... var); public abstract void writeException(int id, Thread thread, Object exception); public abstract void writeCatch(int id, Thread thread, Object exception); public abstract void close(); /** * Increases the depth of the stack for a given thread * * @param thread Thread to increase depth */ protected int incCallDepth(Thread thread) { if (callDeep == null) callDeep = new HashMap<Thread, Integer>(); if (callDeep.containsKey(thread)) { int c = callDeep.get(thread) + 1; callDeep.put(thread, c); return c; } else { callDeep.put(thread, 1); return 1; } } /** * Resets the depth of the stack for a given thread * * @param thread Thread to reset depth */ public void resetCallDepth(Thread thread) { if (callDeep != null && callDeep.containsKey(thread)) callDeep.remove(thread); } /** * Decreases the depth of the stack for a given thread * * @param thread Thread to decrease depth */ protected int decCallDepth(Thread thread) { Integer deep = callDeep.get(thread); if (deep != null && deep > 0) { deep--; callDeep.put(thread, deep); return deep; } return 0; } /** * Gets a boolean value indicating if the methods of a thread are to be logged. * * @param thread Log this thread? * @return True if log, false otherwise */ protected boolean getLogMethod(Thread thread) { return logMethod == null || !logMethod.containsKey(thread) || logMethod.get(thread); } protected void stopLogMethod(Thread thread) { logMethod.put(thread, false); } protected void startLogMethod(Thread thread) { logMethod.put(thread, true); } /** * Initializes the directory where the files for each thread are going to be stored */ protected void initDir(String logDir) { try { BufferedReader reader = new BufferedReader(new FileReader(logDir)); dir = new File("log" + reader.readLine()); } catch (IOException e) { dir = new File("log"); } //Log.debug("LOG DIR:" + dir.getAbsolutePath()); dir.mkdir(); } /** * Returns the file name of the file where this thread's log is being stored * * @param thread * @return Relative filename of the file where this thread's log is being stored */ protected String getThreadFileName(Thread thread) { return "log" + thread.getName(); } /** * A print string representation for an Object o * * @param o Object that is going to be printed * @return A string representation of the object */ protected String printString(Object o) { String string; if (o == null) string = "null"; else if (o instanceof Object[]) { Object[] array = (Object[]) o; int iMax = array.length - 1; if (iMax == -1) return "[]"; StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { b.append(printString(array[i])); if (i == iMax) return b.append(']').toString(); b.append(", "); } } else string = o.toString(); return string; } protected String buildVars(Thread thread, String separator, String simpleSeparator, Object[] vars) { StringBuilder varsString = new StringBuilder(); stopLogMethod(thread); Map<String, String> previousVars = previousVarLog.get(thread); for (int i = 0; i < vars.length / 2; i = i + 2) { StringBuilder tmp = new StringBuilder(); try { String varName = vars[i].toString(); String value; if (vars[i + 1] == null) { value = "null"; } else { value = vars[i + 1].toString(); } String previousValue = previousVars.get(varName); if (!value.equals(previousValue)) { previousVars.put(varName, value); tmp.append(separator); tmp.append(varName); tmp.append(simpleSeparator); tmp.append(value); varsString.append(tmp); } } catch (Exception e) { } } startLogMethod(thread); return varsString.toString(); } public void startLogging(Thread thread, String id) { if (partialLoggingThread == null) { partialLoggingThread = new HashSet(); } partialLoggingThread.add(thread); writeStartLogging(thread, id); } protected abstract void writeStartLogging(Thread thread, String id); protected boolean log(Thread thread) { return !getPartialLogging() || (partialLoggingThread != null && partialLoggingThread.contains(thread)); } protected boolean getPartialLogging() { if (partialLogging == null) { try { BufferedReader reader = new BufferedReader(new FileReader("log/partialLogging")); partialLogging = Boolean.parseBoolean(reader.readLine()); } catch (IOException e) { partialLogging = false; } } return partialLogging; } public String getLogDir() { return logDir; } public void setLogDir(String logDir) { this.logDir = logDir; } /** * Logs the completion of a tests */ public abstract void writeTestFinish(); public abstract void testCount(String signature); public abstract void assertCount(String signature); protected synchronized HashMap<String, Integer> getTransplantPointCallCount() { return transplantPointCallCount; } protected synchronized void setTransplantPointCallCount(HashMap<String, Integer> value) { transplantPointCallCount = value; } protected synchronized HashMap<String, Integer> getAssertCallCount() { return assertCallCount; } public synchronized void setAssertCallCount(HashMap<String, Integer> assertCallCount) { this.assertCallCount = assertCallCount; } public InstruLogWriter getParent() { return parent; } //Thread containing the test public Thread getThread() { return thread; } public synchronized Integer[] getDepthArray(String key) { if (depthInstruArray == null) depthInstruArray = new HashMap<String, Integer[]>(); return depthInstruArray.get(key); } public synchronized void setDepthArray(String id, int index, int value) { Integer[] arr = depthInstruArray.get(id); if (arr == null) { arr = new Integer[] {-1,-1,-1,-1,-1,-1}; depthInstruArray.put(id, arr); } arr[index] = value; } public synchronized void setDepthArray(String id, int min, int mean, int max, int sMin, int sMean, int sMax) { Integer[] arr = depthInstruArray.get(id); if (arr == null) { arr = new Integer[6]; depthInstruArray.put(id, arr); } arr[0] = min; arr[1] = mean; arr[2] = max; arr[3] = sMin; arr[4] = sMean; arr[5] = sMax; } protected String getCurrentTestSignature() { if (parent != null) { return parent.getCurrentTestSignature(); } return currentTestSignature; } protected void setCurrentTestSignature(String currentTestSignature) { this.currentTestSignature = currentTestSignature; } public void depthOnlyMethodCall(Thread thread) { incCallDepth(thread); } }