Mercurial > hg > Members > kono > jpf-core
view src/main/gov/nasa/jpf/listener/NullTracker.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children |
line wrap: on
line source
/* * Copyright (C) 2014, United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. * * The Java Pathfinder core (jpf-core) platform is licensed under the * Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gov.nasa.jpf.listener; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; import gov.nasa.jpf.ListenerAdapter; import gov.nasa.jpf.jvm.bytecode.ARETURN; import gov.nasa.jpf.jvm.bytecode.ASTORE; import gov.nasa.jpf.jvm.bytecode.PUTFIELD; import gov.nasa.jpf.jvm.bytecode.PUTSTATIC; import gov.nasa.jpf.jvm.bytecode.RETURN; import gov.nasa.jpf.report.ConsolePublisher; import gov.nasa.jpf.report.Publisher; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.FieldInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.LocalVarInfo; import gov.nasa.jpf.vm.MJIEnv; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.VM; import gov.nasa.jpf.vm.bytecode.FieldInstruction; import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction; import gov.nasa.jpf.vm.bytecode.InstanceInvokeInstruction; import gov.nasa.jpf.vm.bytecode.InstructionInterface; import gov.nasa.jpf.vm.bytecode.InvokeInstruction; import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction; import gov.nasa.jpf.vm.bytecode.ReturnInstruction; import gov.nasa.jpf.vm.bytecode.ReturnValueInstruction; import gov.nasa.jpf.vm.bytecode.WriteInstruction; import java.io.PrintWriter; /** * trace where nulls come from - which is either a GETFIELD/STATIC, an * JVMInvokeInstruction, an LocalVariableInstruction or a missing init. * * Record/accumulate the causes in an attribute and use the attribute * to explain NPEs */ public class NullTracker extends ListenerAdapter { public static abstract class NullSource { protected InstructionInterface insn; protected ThreadInfo ti; protected ElementInfo ei; protected NullSource cause; NullSource (ThreadInfo ti, InstructionInterface insn, ElementInfo ei){ this.ti = ti; this.insn = insn; this.ei = ei; } public void setCause (NullSource cause){ this.cause = cause; } abstract void printOn (PrintWriter pw); void printInsnOn (PrintWriter pw){ pw.printf(" instruction: [%04x] %s\n", insn.getPosition(), insn.toString()); } void printThreadInfoOn (PrintWriter pw){ pw.println(" executed by: " + ti.getName() + " (id=" + ti.getId() + ")"); } void printMethodInfoOn (PrintWriter pw, String msg, InstructionInterface instruction){ MethodInfo mi = instruction.getMethodInfo(); ClassInfo ci = mi.getClassInfo(); pw.println( msg + ci.getName() + '.' + mi.getLongName() + " (" + instruction.getFilePos() + ')'); } void printCauseOn (PrintWriter pw){ if (cause != null){ pw.println("set by: "); cause.printOn(pw); } } } public static class LocalSource extends NullSource { protected LocalVarInfo local; public LocalSource (ThreadInfo ti, LocalVariableInstruction insn, LocalVarInfo local){ super(ti, insn, null); this.local = local; } @Override void printOn (PrintWriter pw){ printInsnOn(pw); if (local != null){ pw.println(" for local: " + local.getName()); } else { pw.println(" for local: #" + ((LocalVariableInstruction)insn).getLocalVariableSlot()); } printMethodInfoOn(pw, " in method: ", insn); printThreadInfoOn(pw); printCauseOn(pw); } } public static class FieldSource extends NullSource { public FieldSource (ThreadInfo ti, FieldInstruction insn, ElementInfo ei){ super(ti,insn,ei); } @Override void printOn (PrintWriter pw){ FieldInfo fi = ((FieldInstruction)insn).getFieldInfo(); MethodInfo mi = insn.getMethodInfo(); printInsnOn(pw); pw.println(" for field: " + fi.getFullName()); printMethodInfoOn(pw, " in method: ", insn); printThreadInfoOn(pw); printCauseOn(pw); } } public static class MethodSource extends NullSource { InvokeInstruction call; public MethodSource (ThreadInfo ti, InstructionInterface returnInsn, InvokeInstruction call, ElementInfo ei){ super(ti,returnInsn,ei); this.call = call; } @Override void printOn (PrintWriter pw){ printInsnOn(pw); printMethodInfoOn(pw, " of method: ", insn); if (ei != null){ pw.println(" for object: " + ei); } printMethodInfoOn(pw, " called by: ", call); printThreadInfoOn(pw); printCauseOn(pw); } } public static class CtorSource extends MethodSource { public CtorSource (ThreadInfo ti, Instruction returnInsn, InvokeInstruction call, ElementInfo ei){ super(ti,returnInsn,call, ei); } @Override void printOn (PrintWriter pw){ printMethodInfoOn(pw, " missing init: ", insn); if (ei != null){ pw.println(" for object: " + ei); } printMethodInfoOn(pw, " called by: ", call); printThreadInfoOn(pw); printCauseOn(pw); } } //--------------------------------------------------------------------------------- protected NullSource nullSource; public NullTracker (Config config, JPF jpf){ jpf.addPublisherExtension(ConsolePublisher.class, this); } protected void checkCtorSourcePre (ThreadInfo ti, ReturnInstruction insn){ MethodInfo mi = insn.getMethodInfo(); if (mi.isCtor()) { StackFrame callerFrame = null; InvokeInstruction call = null; ElementInfo ei = ti.getThisElementInfo(); ClassInfo ci = ei.getClassInfo(); int nInstance = ci.getNumberOfDeclaredInstanceFields(); for (int i = 0; i < nInstance; i++) { FieldInfo fi = ci.getDeclaredInstanceField(i); if (fi.isReference()) { int ref = ei.getReferenceField(fi); if (ref == MJIEnv.NULL) { ei = ei.getModifiableInstance(); // why do we need this in a ctor? if (call == null) { callerFrame = ti.getCallerStackFrame(); call = (InvokeInstruction) callerFrame.getPC(); } NullSource attr = new CtorSource(ti, insn, call, ti.getThisElementInfo()); ei.setFieldAttr(fi, attr); } } } } } protected void checkFieldSourcePre (ThreadInfo ti, WriteInstruction put){ FieldInfo fi = put.getFieldInfo(); if (fi.isReference()) { StackFrame frame = ti.getTopFrame(); int valSlot = put.getValueSlot(frame); int ref = frame.getSlot(valSlot); if (ref == MJIEnv.NULL) { // field will be set to null ElementInfo ei = put.getElementInfo(ti); NullSource attr = new FieldSource(ti, (FieldInstruction)put, ei); NullSource cause = frame.getSlotAttr(valSlot, NullSource.class); if (cause != null) { attr.setCause(cause); frame.replaceSlotAttr(valSlot, cause, attr); } else { frame.addSlotAttr(valSlot, attr); } } } } protected void checkMethodSourcePre (ThreadInfo ti, ReturnValueInstruction aret){ StackFrame frame = ti.getTopFrame(); int valSlot = aret.getValueSlot(frame); int ref = frame.getSlot(valSlot); if (ref == MJIEnv.NULL) { StackFrame callerFrame = ti.getCallerStackFrame(); InvokeInstruction call = (InvokeInstruction) callerFrame.getPC(); NullSource attr = new MethodSource(ti, aret, call, ti.getThisElementInfo()); NullSource cause = frame.getSlotAttr(valSlot, NullSource.class); if (cause != null) { attr.setCause(cause); frame.replaceSlotAttr(valSlot,cause, attr); } else { frame.addSlotAttr(valSlot,attr); } } } @Override public void executeInstruction (VM vm, ThreadInfo ti, Instruction insn) { if (insn instanceof ARETURN){ checkMethodSourcePre( ti, (ARETURN)insn); } else if (insn instanceof PUTFIELD || insn instanceof PUTSTATIC){ checkFieldSourcePre( ti, (WriteInstruction) insn); } else if (insn instanceof RETURN){ checkCtorSourcePre(ti, (RETURN) insn); } } protected void checkLocalSourcePost (ThreadInfo ti, LocalVariableInstruction insn){ int slotIdx = insn.getLocalVariableSlot(); StackFrame frame = ti.getTopFrame(); int ref = frame.getSlot(slotIdx); if (ref == MJIEnv.NULL) { LocalVarInfo lv = insn.getLocalVarInfo(); NullSource attr = new LocalSource(ti, insn, lv); NullSource cause = frame.getSlotAttr(slotIdx, NullSource.class); if (cause != null) { attr.setCause(cause); frame.replaceSlotAttr(slotIdx, cause, attr); } else { frame.addSlotAttr(slotIdx, attr); } } } @Override public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction insn){ // we need to do LocalVariableInstruction post exec since it did overwrite the attr if it had an immediate operand if (insn instanceof ASTORE) { checkLocalSourcePost( ti, (LocalVariableInstruction)insn); } } @Override public void exceptionThrown(VM vm, ThreadInfo ti, ElementInfo thrownException) { if (thrownException.instanceOf("Ljava/lang/NullPointerException;")){ StackFrame frame = ti.getTopFrame(); Instruction insn = ti.getPC(); if (insn instanceof InstanceFieldInstruction){ // field access on null object int objSlot = ((InstanceFieldInstruction)insn).getObjectSlot(frame); NullSource attr = frame.getSlotAttr( objSlot,NullSource.class); if (attr != null) { nullSource = attr; } } else if (insn instanceof InstanceInvokeInstruction) { // call on a null object int objSlot = ((InstanceInvokeInstruction)insn).getObjectSlot(frame); NullSource attr = frame.getSlotAttr( objSlot, NullSource.class); if (attr != null) { nullSource = attr; } } } } @Override public void publishPropertyViolation (Publisher publisher) { if (nullSource != null){ // otherwise we don't have anything to report PrintWriter pw = publisher.getOut(); publisher.publishTopicStart("NullTracker " + publisher.getLastErrorId()); pw.println("null value set by: "); nullSource.printOn(pw); } } }