Java tutorial
/** License information: * Component: checkedcoverage-traceReader * Package: pku.sei.checkedcoverage.traceResult * Class: TraceResult * Filename: checkedcoverage-traceReader/src/main/java/de/unisb/cs/st/checkedcoverage/traceResult/TraceResult.java * * This file is part of the checkedcoverage tool, developed by Clemens Hammacher at Saarland University. * See http://www.st.cs.uni-saarland.de/checkedcoverage/ for more information. * * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a * letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. */ package pku.sei.checkedcoverage.traceResult; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.io.PushbackInputStream; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.zip.GZIPInputStream; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang.StringUtils; import de.hammacher.util.MultiplexedFileReader; import de.hammacher.util.MultiplexedFileReader.MultiplexInputStream; import de.hammacher.util.StringCacheInput; import pku.sei.checkedcoverage.common.classRepresentation.AbstractInstructionInstance; import pku.sei.checkedcoverage.common.classRepresentation.AbstractInstructionInstanceFactory; import pku.sei.checkedcoverage.common.classRepresentation.Instruction; import pku.sei.checkedcoverage.common.classRepresentation.InstructionInstance; import pku.sei.checkedcoverage.common.classRepresentation.InstructionInstanceFactory; import pku.sei.checkedcoverage.common.classRepresentation.ReadClass; import pku.sei.checkedcoverage.common.classRepresentation.ReadMethod; import pku.sei.checkedcoverage.common.classRepresentation.instructions.AbstractInstruction; import pku.sei.checkedcoverage.common.exceptions.TracerException; import pku.sei.checkedcoverage.common.progress.ConsoleProgressMonitor; import pku.sei.checkedcoverage.common.progress.ProgressMonitor; import pku.sei.checkedcoverage.coverage.utils.Utils; public class TraceResult { private final static class ThreadIdList extends AbstractList<ThreadId> { private final List<ThreadTraceResult> wrappedThreadTraces; public ThreadIdList(List<ThreadTraceResult> threadTraces) { this.wrappedThreadTraces = threadTraces; } @Override public ThreadId get(final int index) { return this.wrappedThreadTraces.get(index).getId(); } @Override public int size() { return this.wrappedThreadTraces.size(); } } private final List<ReadClass> readClasses; private final List<ThreadTraceResult> threadTraces; private final Instruction[] instructions; private static HashSet<String> slicingcris = new HashSet<String>(); private static HashMap<String, HashSet<Instruction>> coveredInsts = new HashMap<String, HashSet<Instruction>>(); public TraceResult(File filename) throws IOException { final MultiplexedFileReader file = new MultiplexedFileReader(filename); if (file.getStreamIds().size() < 2) throw new IOException("corrupted data"); final MultiplexInputStream readClassesStream = file.getInputStream(0); if (readClassesStream == null) throw new IOException("corrupted data"); PushbackInputStream pushBackInput = new PushbackInputStream( new BufferedInputStream(new GZIPInputStream(readClassesStream, 512), 512), 1); final DataInputStream readClassesInputStream = new DataInputStream(pushBackInput); final ArrayList<ReadClass> readClasses0 = new ArrayList<ReadClass>(); final StringCacheInput stringCache = new StringCacheInput(); int testRead; while ((testRead = pushBackInput.read()) != -1) { pushBackInput.unread(testRead); readClasses0.add(ReadClass.readFrom(readClassesInputStream, stringCache)); } readClasses0.trimToSize(); Collections.sort(readClasses0); this.readClasses = readClasses0; this.instructions = getInstructionArray(readClasses0); final MultiplexInputStream threadTracersStream = file.getInputStream(1); if (threadTracersStream == null) throw new IOException("corrupted data"); pushBackInput = new PushbackInputStream( new BufferedInputStream(new GZIPInputStream(threadTracersStream, 512), 512), 1); final DataInputStream threadTracersInputStream = new DataInputStream(pushBackInput); final ArrayList<ThreadTraceResult> threadTraces0 = new ArrayList<ThreadTraceResult>(); while ((testRead = pushBackInput.read()) != -1) { pushBackInput.unread(testRead); threadTraces0.add(ThreadTraceResult.readFrom(threadTracersInputStream, this, file)); } threadTraces0.trimToSize(); Collections.sort(threadTraces0); this.threadTraces = threadTraces0; } private static Instruction[] getInstructionArray(final List<ReadClass> classes) throws IOException { int numInstructions = 0; for (final ReadClass c : classes) if (c.getInstructionNumberEnd() > numInstructions) numInstructions = c.getInstructionNumberEnd(); final Instruction[] instructions = new Instruction[numInstructions]; for (final ReadClass c : classes) { for (final ReadMethod m : c.getMethods()) { for (final AbstractInstruction instr : m.getInstructions()) { if (instructions[instr.getIndex()] != null) throw new IOException("Same instruction index given twice."); instructions[instr.getIndex()] = instr; } } } return instructions; } public static TraceResult readFrom(final File filename) throws IOException { return new TraceResult(filename); } /** * Returns an iterator that iterates backwards through the execution trace. * * This iteration is very cheap since no information has to be cached (in * contrast to the Iterator returned by * {@link #getForwardIterator(ThreadId, InstructionInstanceFactory)}. The * trace is generated while reading in the trace file. * * @param threadId * the identifier of the thread whose execution trace iterator is * requested * @param filter * a filter to ignore certain instruction instances. may be * <code>null</code>. * @param instanceFactory * a factory that creates the instruction instance objects * @return an iterator that iterates backwards through the execution trace. * the iterator extends {@link Iterator} over * {@link InstructionInstance}. */ public <InstanceType extends InstructionInstance> BackwardTraceIterator<InstanceType> getBackwardIterator( final ThreadId threadId, final InstanceFilter<? super InstanceType> filter, InstructionInstanceFactory<? extends InstanceType> instanceFactory) { final ThreadTraceResult res = findThreadTraceResult(threadId); return res == null ? null : res.getBackwardIterator(filter, instanceFactory); } /** * @see #getBackwardIterator(ThreadId, InstanceFilter) */ public BackwardTraceIterator<InstructionInstance> getBackwardIterator(final ThreadId threadId, final InstanceFilter<? super InstructionInstance> filter) { InstructionInstanceFactory<? extends InstructionInstance> factory = new AbstractInstructionInstanceFactory(); return getBackwardIterator(threadId, filter, factory); } /** * Returns an iterator that iterates backwards through the execution trace. * * This iteration is very cheap since no information has to be cached (in * contrast to the Iterator returned by * {@link #getForwardIterator(ThreadId, InstructionInstanceFactory)}. The * trace is generated while reading in the trace file. * * @param javaThreadId * the java thread id of the thread whose execution trace * iterator is requested * @param filter * a filter to ignore certain instruction instances. may be * <code>null</code>. * @return an iterator that iterates backwards through the execution trace. * the iterator extends {@link Iterator} over * {@link InstructionInstance}. */ public BackwardTraceIterator<AbstractInstructionInstance> getBackwardIterator(final long javaThreadId, InstanceFilter<? super AbstractInstructionInstance> filter) { final ThreadId id = getThreadId(javaThreadId); return id == null ? null : getBackwardIterator(id, filter, new AbstractInstructionInstanceFactory()); } /** * Returns an iterator that is able to iterate in any direction through the * execution trace. * * This iteration is usually much more expensive (especially with respect to * memory consumption) than the Iterator returned by * {@link #getBackwardIterator(long, InstanceFilter)}. So whenever you just * need to iterate backwards, you should use that backward iterator. * * @param threadId * the identifier of the thread whose execution trace iterator is * requested * @param instanceFactory * a factory which is used to create the instruction instance * objects. may be used to return special objects which can be * annotated by the user of this function. * @return an iterator that is able to iterate in any direction through the * execution trace. the iterator extends {@link ListIterator} over * {@link InstructionInstance}. */ public <InstanceType extends InstructionInstance> ForwardTraceIterator<InstanceType> getForwardIterator( final ThreadId threadId, InstructionInstanceFactory<InstanceType> instanceFactory) { final ThreadTraceResult res = findThreadTraceResult(threadId); return res == null ? null : res.getForwardIterator(instanceFactory); } /** * Returns an iterator that is able to iterate in any direction through the * execution trace. * * This iteration is usually much more expensive (especially with respect to * memory consumption) than the Iterator returned by * {@link #getBackwardIterator(long, InstanceFilter)}. So whenever you just * need to iterate backwards, you should use that backward iterator. * * @param javaThreadId * the java thread id of the thread whose execution trace * iterator is requested * @param instanceFactory * a factory which is used to create the instruction instance * objects. may be used to return special objects which can be * annotated by the user of this function. * @return an iterator that is able to iterate in any direction through the * execution trace. the iterator extends {@link ListIterator} over * {@link InstructionInstance}. */ public <InstanceType extends InstructionInstance> ForwardTraceIterator<InstanceType> getIterator( final long javaThreadId, InstructionInstanceFactory<InstanceType> instanceFactory) { final ThreadId id = getThreadId(javaThreadId); return id == null ? null : getForwardIterator(id, instanceFactory); } private ThreadTraceResult findThreadTraceResult(final ThreadId threadId) { // binary search int left = 0; int right = this.threadTraces.size(); int mid; while ((mid = (left + right) / 2) != left) { final ThreadTraceResult midVal = this.threadTraces.get(mid); if (midVal.getId().compareTo(threadId) <= 0) left = mid; else right = mid; } final ThreadTraceResult found = this.threadTraces.get(mid); return found.getId().compareTo(threadId) == 0 ? found : null; } /** * Returns a sorted List of all threads that are represented by traces in * this TraceResult. * * @return the sorted list of {@link ThreadId}s. */ public List<ThreadId> getThreads() { return new ThreadIdList(this.threadTraces); } /** * Returns a sorted List of all {@link ReadClass}es. * * @return a sorted List of all {@link ReadClass}es */ public List<ReadClass> getReadClasses() { return Collections.unmodifiableList(this.readClasses); } /** * Search for the {@link ReadClass} with the given class name. * * @param name * the java class name to search for (e.g. java.lang.String) * @return the {@link ReadClass} with the given class name, or * <code>null</code> if this TraceResult does not contain a * ReadClass with that name */ public ReadClass findReadClass(String name) { // binary search int left = 0; int right = this.readClasses.size(); int mid; while ((mid = (left + right) / 2) != left) { final ReadClass midVal = this.readClasses.get(mid); int cmp = midVal.getName().compareTo(name); if (cmp < 0) left = mid; else if (cmp == 0) return midVal; else right = mid; } final ReadClass found = this.readClasses.get(mid); return found.getName().equals(name) ? found : null; } /** * Search for the {@link ReadClass} with the given internal class name. * * @param internalName * the internal class name to search for (e.g. java/lang/String) * @return the {@link ReadClass} with the given class name, or * <code>null</code> if this TraceResult does not contain a * ReadClass with that name */ public ReadClass findReadClassByInternalName(String internalName) { // binary search int left = 0; int right = this.readClasses.size(); int mid; while ((mid = (left + right) / 2) != left) { ReadClass midVal = this.readClasses.get(mid); int cmp = midVal.getInternalClassName().compareTo(internalName); if (cmp < 0) left = mid; else if (cmp == 0) return midVal; else right = mid; } ReadClass found = this.readClasses.get(mid); return found.getInternalClassName().equals(internalName) ? found : null; } public Instruction getInstruction(final int index) { if (index < 0 || index >= this.instructions.length) return null; return this.instructions[index]; } /** * The previous main method. * * @param args */ public static void previousMain(final String[] args) { Options options = createOptions(); CommandLineParser parser = new GnuParser(); CommandLine cmdLine; try { cmdLine = parser.parse(options, args, true); } catch (ParseException e) { System.err.println("Error parsing the command line arguments: " + e.getMessage()); return; } if (cmdLine.hasOption('h')) { printHelp(options, System.out); System.exit(0); } InstanceFilter<InstructionInstance> filter; if (cmdLine.hasOption("filter")) { if ("labels".equals(cmdLine.getOptionValue("filter"))) { filter = InstanceFilter.LabelFilter.instance; } else if ("additionals".equals(cmdLine.getOptionValue("filter"))) { filter = InstanceFilter.AdditionalLabelFilter.instance; } else if ("none".equals(cmdLine.getOptionValue("filter"))) { filter = null; } else { System.err.println("Illegal argument for filter: " + cmdLine.getOptionValue("filter")); return; } } else { // default: filter = InstanceFilter.AdditionalLabelFilter.instance; } String[] additionalArgs = cmdLine.getArgs(); if (additionalArgs.length != 1) { System.err.println("Error: No input file given."); printHelp(options, System.err); System.exit(-1); } final File traceFile = new File(additionalArgs[0]); Long threadToTrace = null; if (cmdLine.hasOption('t')) { try { threadToTrace = Long.parseLong(cmdLine.getOptionValue('t')); } catch (final NumberFormatException e) { System.err.println("Illegal thread id: " + cmdLine.getOptionValue('t')); System.exit(-1); } } System.out.println("Opening and reading trace file..."); TraceResult tr = null; try { tr = readFrom(traceFile); } catch (final IOException e) { System.err.println("Error opening trace file: " + e); System.exit(-1); return; } final List<ThreadId> threads = tr.getThreads(); if (threads.size() == 0) { System.err.println("The trace file contains no tracing information."); System.exit(-1); } System.out.println("The trace file contains traces for these threads:"); ThreadId tracing = null; for (final ThreadId t : threads) { if (threadToTrace == null) { if ("main".equals(t.getThreadName()) && (tracing == null || t.getJavaThreadId() < tracing.getJavaThreadId())) tracing = t; } else if (t.getJavaThreadId() == threadToTrace.longValue()) { tracing = t; } System.out.format("%15d: %s%n", t.getJavaThreadId(), t.getThreadName()); } if (tracing == null) { System.out.println(threadToTrace == null ? "Couldn't find a main thread." : "The thread you selected was not found."); System.exit(-1); return; } System.out.println(threadToTrace == null ? "Selected:" : "You selected:"); System.out.format("%15d: %s%n", tracing.getJavaThreadId(), tracing.getThreadName()); try { if (cmdLine.hasOption("length")) { final BackwardTraceIterator<AbstractInstructionInstance> it = tr.getBackwardIterator(tracing, filter, new AbstractInstructionInstanceFactory()); ProgressMonitor monitor = null; if (cmdLine.hasOption("--progress")) { monitor = new ConsoleProgressMonitor(System.out, "Computing trace length", true, 100, true, true); monitor.start(it); } try { while (it.hasNext()) it.next(); } finally { if (monitor != null) monitor.end(); } System.out.format( "%nNumber of instructions: %d (+ %d additional = %d total instructions)%nReady%n", it.getNumInstructions(), it.getNumFilteredInstructions(), it.getNumInstructions() + it.getNumFilteredInstructions()); } else { System.out.println("The backward trace:"); BackwardTraceIterator<AbstractInstructionInstance> it = tr.getBackwardIterator(tracing, filter, new AbstractInstructionInstanceFactory()); long nr = 0; String format = "%8d (%8d) %-100s -> %3d %7d %s%n"; System.out.format("%19s %-100s %3s %7s %s%n", "Nr ( intern)", "Location", "Dep", "OccNr", "Instruction"); while (it.hasNext()) { InstructionInstance inst = it.next(); ReadMethod method = inst.getInstruction().getMethod(); ReadClass class0 = method.getReadClass(); if (inst.getInstruction().getType().equals("VAR") || (inst.getInstruction().getType().equals("FIELD"))) { System.out.println(inst.getInstruction().toString()); } System.out.format(format, nr++, inst.getInstanceNr(), class0.getName() + "." + method.getName() + ":" + inst.getInstruction().getLineNumber(), inst.getStackDepth(), inst.getOccurrenceNumber(), inst.toString()); } System.out.format( "%nNumber of instructions: %d (+ %d additional = %d total instructions)%nReady%n", it.getNumInstructions(), it.getNumFilteredInstructions(), it.getNumInstructions() + it.getNumFilteredInstructions()); } } catch (final TracerException e) { System.err.print("Error while tracing: "); e.printStackTrace(System.err); System.exit(-1); } } public static HashSet<String> getCriterions() { if (slicingcris.size() == 0) calc(); return slicingcris; } public static String getCriterion() { return StringUtils.join(getCriterions().toArray(), ","); } public static void calc() { InstanceFilter<InstructionInstance> filter; filter = InstanceFilter.AdditionalLabelFilter.instance; final File traceFile = new File(Utils.trace_file); System.out.println("\n\nAnalyzing trace phase:"); System.out.println("Opening and reading trace file..." + Utils.trace_file); TraceResult tr = null; try { tr = readFrom(traceFile); } catch (final IOException e) { System.err.println("Error opening trace file:" + e); System.exit(-1); } final List<ThreadId> threads = tr.getThreads(); if (threads.size() == 0) { System.err.println("The trace file contains no tracing information."); System.exit(-1); } ThreadId tracing = null; for (final ThreadId t : threads) { if ("main".equals(t.getThreadName()) && (tracing == null || t.getJavaThreadId() < tracing.getJavaThreadId())) tracing = t; } if (tracing == null) { System.out.println("Couldn't find a main thread."); System.exit(-1); } try { BackwardTraceIterator<AbstractInstructionInstance> it = tr.getBackwardIterator(tracing, filter, new AbstractInstructionInstanceFactory()); String format = "(%8d) %-100s -> %s type:%s%n"; while (it.hasNext()) { InstructionInstance inst = it.next(); ReadMethod method = inst.getInstruction().getMethod(); ReadClass class0 = method.getReadClass(); //add slicing criterions. if (Utils.TESTS.contains(class0.getName()) && method.getName().toLowerCase().contains("test")) { String cri = class0.getName() + "." + method.getName() + ":" + inst.getInstruction().getLineNumber(); slicingcris.add(cri + ":*"); } //get local variables if (class0.getName().startsWith(Utils.focused_package)) { if (!coveredInsts.containsKey(class0.getName())) { coveredInsts.put(class0.getName(), new HashSet<Instruction>()); } // System.out.format(format, inst.getInstanceNr(), // class0.getName() + "." + method.getName() + ":" // + inst.getInstruction().getLineNumber(), //// inst.getStackDepth(), inst.getOccurrenceNumber(), // inst.toString(), inst.getInstruction().getType().toString()); coveredInsts.get(class0.getName()).add(inst.getInstruction()); // if(inst.getInstruction().getType().equals(InstructionType.FIELD) || // inst.getInstruction().getType().equals(InstructionType.VAR) ){ // if(inst.getInstruction().getType().equals(InstructionType.FIELD)){ // FieldInstruction fieldinst = (FieldInstruction) inst.getInstruction(); // System.out.println("filed:"+fieldinst.getFieldName()+":"+fieldinst.getLineNumber()); // } // else if(inst.getInstruction().getType().equals(InstructionType.VAR)){ // VarInstruction varinst = (VarInstruction) inst.getInstruction(); // if(varinst.getOpcode() == Opcodes.DSTORE || // varinst.getOpcode() == Opcodes.ASTORE || // varinst.getOpcode() == Opcodes.LSTORE || // varinst.getOpcode() == Opcodes.ISTORE || // varinst.getOpcode() == Opcodes.FSTORE || // varinst.getOpcode() == Opcodes.RET ){ // System.out.println("var:"+method.getLocalVariables()[varinst.getLocalVarIndex()]+ // ":at "+varinst.getLineNumber()+"inst: "+ // varinst.toString()); // } //// System.out.println("var:"+varinst.getLocalVarIndex()+":"+varinst.getLineNumber()); //// System.out.println(method.getLocalVariables()[varinst.getLocalVarIndex()]); // } // else{ // System.out.println("cannot parse the variable info correctly, the type of instruction is:" // + inst.getInstruction().getType()); // } // // } } } } catch (final TracerException e) { System.err.print("Error while tracing: "); e.printStackTrace(System.err); System.exit(-1); } } /** * read from trace file, get all covered lines in methodsList * * @param file_name * , the trace file name * @param classList * , the set of classes to be focused. * @return the map of class name->set of instructions. */ public static HashMap<String, HashSet<Instruction>> getCoveredInsts() { if (coveredInsts.size() == 0) calc(); return coveredInsts; } /** // * print covered lines in each classes // * // * @param coveredLines // */ // private static void printCoveredLines( // HashMap<String, HashMap<Integer, String>> methodCoveredLines) { // Iterator<String> it = methodCoveredLines.keySet().iterator(); // while (it.hasNext()) { // String key = it.next(); // System.out.println("covered lines in:" + key); // HashMap<Integer, String> map = methodCoveredLines.get(key); // for (Integer line : map.keySet()) { // System.out.println(line + "in method:" + map.get(line)); // } // } // } public static void main(final String[] args) { calc(); System.out.println(getCriterion()); // HashMap<String, HashMap<Integer, String>> coveredLines = getCoveredLines( // "/Users/muyao/Workspace/checkedcoverage/test.trace", "example"); // printCoveredLines(coveredLines); // System.out.println(); } @SuppressWarnings("static-access") private static Options createOptions() { Options options = new Options(); options.addOption(OptionBuilder.isRequired(false).withArgName("threadid").hasArg(true) .withDescription("thread id to select for trace output (default: main thread)") .withLongOpt("threadid").create('t')); options.addOption(OptionBuilder.isRequired(false).hasArg(false) .withDescription("do only output the trace length").withLongOpt("length").create('l')); options.addOption(OptionBuilder.isRequired(false).hasArg(false) .withDescription( "show progress while computing the trace length (only affectfull together with --length)") .withLongOpt("progress").create('p')); options.addOption(OptionBuilder.isRequired(false).hasArg(false).withDescription("print this help and exit") .withLongOpt("help").create('h')); options.addOption(OptionBuilder.isRequired(false).withArgName("filter").hasArg(true) .withDescription( "(none/labels/additionals) which instructions to filter out (default: additionals)") .withLongOpt("filter").create('f')); return options; } private static void printHelp(Options options, PrintStream out) { out.println("Usage: " + TraceResult.class.getSimpleName() + " [<options>] <file>"); out.println("where <file> is the input trace file, and <options> may be one or more of"); HelpFormatter formatter = new HelpFormatter(); PrintWriter pw = new PrintWriter(out, true); formatter.printOptions(pw, 120, options, 5, 3); } public ThreadId getThreadId(final long javaThreadId) { // binary search int left = 0; int right = this.threadTraces.size(); int mid; while ((mid = (left + right) / 2) != left) { final ThreadTraceResult midVal = this.threadTraces.get(mid); if (midVal.getJavaThreadId() <= javaThreadId) left = mid; else right = mid; } final ThreadId found = this.threadTraces.get(mid).getId(); return found.getJavaThreadId() == javaThreadId ? found : null; } }