//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.listener;
import java.io.PrintWriter;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.ListenerAdapter;
import gov.nasa.jpf.annotation.JPFOption;
import gov.nasa.jpf.jvm.bytecode.FieldInstruction;
import gov.nasa.jpf.jvm.bytecode.InstanceFieldInstruction;
import gov.nasa.jpf.jvm.bytecode.InvokeInstruction;
import gov.nasa.jpf.jvm.bytecode.LockInstruction;
import gov.nasa.jpf.search.Search;
import gov.nasa.jpf.vm.ChoiceGenerator;
import gov.nasa.jpf.vm.ClassInfo;
import gov.nasa.jpf.vm.ElementInfo;
import gov.nasa.jpf.vm.Instruction;
import gov.nasa.jpf.vm.MethodInfo;
import gov.nasa.jpf.vm.ThreadInfo;
import gov.nasa.jpf.vm.VM;
/**
* Listener tool to monitor JPF execution. This class can be used as a drop-in
* replacement for JPF, which is called by ExecTracker.
* ExecTracker is mostly a VMListener of 'instructionExecuted' and
* a SearchListener of 'stateAdvanced' and 'statehBacktracked'
*/
public class ExecTracker extends ListenerAdapter {
@JPFOption(type = "Boolean", key = "et.print_insn", defaultValue = "true", comment = "print executed bytecode instructions")
boolean printInsn = true;
@JPFOption(type = "Boolean", key = "et.print_src", defaultValue = "false", comment = "print source lines")
boolean printSrc = false;
@JPFOption(type = "Boolean", key = "et.print_mth", defaultValue = "false", comment = "print executed method names")
boolean printMth = false;
@JPFOption(type = "Boolean", key = "et.skip_init", defaultValue = "true", comment = "do not log execution before entering main()")
boolean skipInit = false;
boolean showShared = false;
PrintWriter out;
String lastLine;
MethodInfo lastMi;
String linePrefix;
boolean skip;
MethodInfo miMain; // just to make init skipping more efficient
public ExecTracker (Config config) {
/** @jpfoption et.print_insn : boolean - print executed bytecode instructions (default=true). */
printInsn = config.getBoolean("et.print_insn", true);
/** @jpfoption et.print_src : boolean - print source lines (default=false). */
printSrc = config.getBoolean("et.print_src", false);
/** @jpfoption et.print_mth : boolean - print executed method names (default=false). */
printMth = config.getBoolean("et.print_mth", false);
/** @jpfoption et.skip_init : boolean - do not log execution before entering main() (default=true). */
skipInit = config.getBoolean("et.skip_init", true);
showShared = config.getBoolean("et.show_shared", true);
if (skipInit) {
skip = true;
}
out = new PrintWriter(System.out, true);
}
/******************************************* SearchListener interface *****/
@Override
public void stateRestored(Search search) {
int id = search.getStateId();
out.println("----------------------------------- [" +
search.getDepth() + "] restored: " + id);
}
//--- the ones we are interested in
@Override
public void searchStarted(Search search) {
out.println("----------------------------------- search started");
if (skipInit) {
ThreadInfo tiCurrent = ThreadInfo.getCurrentThread();
miMain = tiCurrent.getEntryMethod();
out.println(" [skipping static init instructions]");
}
}
@Override
public void stateAdvanced(Search search) {
int id = search.getStateId();
out.print("----------------------------------- [" +
search.getDepth() + "] forward: " + id);
if (search.isNewState()) {
out.print(" new");
} else {
out.print(" visited");
}
if (search.isEndState()) {
out.print(" end");
}
out.println();
lastLine = null; // in case we report by source line
lastMi = null;
linePrefix = null;
}
@Override
public void stateProcessed (Search search) {
int id = search.getStateId();
out.println("----------------------------------- [" +
search.getDepth() + "] done: " + id);
}
@Override
public void stateBacktracked(Search search) {
int id = search.getStateId();
lastLine = null;
lastMi = null;
out.println("----------------------------------- [" +
search.getDepth() + "] backtrack: " + id);
}
@Override
public void searchFinished(Search search) {
out.println("----------------------------------- search finished");
}
/******************************************* VMListener interface *********/
@Override
public void gcEnd(VM vm) {
out.println("\t\t # garbage collection");
}
//--- the ones we are interested in
@Override
public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
if (skip) {
MethodInfo mi = executedInsn.getMethodInfo();
if (mi == miMain) {
skip = false; // start recording
} else {
return; // skip
}
}
int nNoSrc = 0;
if (linePrefix == null) {
linePrefix = Integer.toString( ti.getId()) + " : ";
}
// that's pretty redundant to what is done in the ConsolePublisher, but we don't want
// presentation functionality in Step anymore
if (printSrc) {
String line = executedInsn.getSourceLine();
if (line != null){
if (nNoSrc > 0) {
out.println(" [" + nNoSrc + " insn w/o sources]");
}
if (!line.equals(lastLine)) {
out.print(" [");
out.print(executedInsn.getFileLocation());
out.print("] : ");
out.println(line.trim());
}
nNoSrc = 0;
} else { // no source
nNoSrc++;
}
lastLine = line;
}
if (printInsn) {
if (printMth) {
MethodInfo mi = executedInsn.getMethodInfo();
if (mi != lastMi){
ClassInfo mci = mi.getClassInfo();
out.print(" ");
if (mci != null) {
out.print(mci.getName());
out.print(".");
}
out.println(mi.getUniqueName());
lastMi = mi;
}
}
out.print( linePrefix);
out.print('[');
out.print(executedInsn.getPosition());
out.print("] ");
out.print(executedInsn);
// annotate (some of) the bytecode insns with their arguments
if (executedInsn instanceof InvokeInstruction) {
MethodInfo callee = ((InvokeInstruction)executedInsn).getInvokedMethod();
if ((callee != null) && callee.isMJI()) { // Huhh? why do we have to check this?
out.print(" [native]");
}
} else if (executedInsn instanceof FieldInstruction) {
out.print(" ");
if (executedInsn instanceof InstanceFieldInstruction){
InstanceFieldInstruction iinsn = (InstanceFieldInstruction)executedInsn;
out.print(iinsn.getId(iinsn.getLastElementInfo()));
} else {
out.print(((FieldInstruction)executedInsn).getVariableId());
}
} else if (executedInsn instanceof LockInstruction) {
LockInstruction lockInsn = (LockInstruction)executedInsn;
int lockRef = lockInsn.getLastLockRef();
out.print(" ");
out.print( ti.getElementInfo(lockRef));
}
out.println();
}
}
@Override
public void threadStarted(VM vm, ThreadInfo ti) {
out.println( "\t\t # thread started: " + ti.getName() + " index: " + ti.getId());
}
@Override
public void threadTerminated(VM vm, ThreadInfo ti) {
out.println( "\t\t # thread terminated: " + ti.getName() + " index: " + ti.getId());
}
@Override
public void exceptionThrown (VM vm, ThreadInfo ti, ElementInfo ei) {
MethodInfo mi = ti.getTopFrameMethodInfo();
out.println("\t\t\t\t # exception: " + ei + " in " + mi);
}
@Override
public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
out.println("\t\t # choice: " + currentCG);
//vm.dumpThreadStates();
}
@Override
public void objectExposed (VM vm, ThreadInfo currentThread, ElementInfo sharedObject, ElementInfo exposedObject) {
if (showShared){
out.println("\t\t # exposed " + exposedObject + " through shared " + sharedObject);
}
}
@Override
public void objectShared (VM vm, ThreadInfo currentThread, ElementInfo sharedObject) {
if (showShared){
out.println("\t\t # shared " + sharedObject);
}
}
/****************************************** private stuff ******/
void filterArgs (String[] args) {
for (int i=0; i<args.length; i++) {
if (args[i] != null) {
if (args[i].equals("-print-lines")) {
printSrc = true;
args[i] = null;
}
}
}
}
}