2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package gov.nasa.jpf.listener;
21 import gov.nasa.jpf.Config;
22 import gov.nasa.jpf.JPF;
23 import gov.nasa.jpf.ListenerAdapter;
24 import gov.nasa.jpf.jvm.bytecode.ARETURN;
25 import gov.nasa.jpf.jvm.bytecode.ASTORE;
26 import gov.nasa.jpf.jvm.bytecode.PUTFIELD;
27 import gov.nasa.jpf.jvm.bytecode.PUTSTATIC;
28 import gov.nasa.jpf.jvm.bytecode.RETURN;
29 import gov.nasa.jpf.report.ConsolePublisher;
30 import gov.nasa.jpf.report.Publisher;
31 import gov.nasa.jpf.vm.ClassInfo;
32 import gov.nasa.jpf.vm.ElementInfo;
33 import gov.nasa.jpf.vm.FieldInfo;
34 import gov.nasa.jpf.vm.Instruction;
35 import gov.nasa.jpf.vm.LocalVarInfo;
36 import gov.nasa.jpf.vm.MJIEnv;
37 import gov.nasa.jpf.vm.MethodInfo;
38 import gov.nasa.jpf.vm.StackFrame;
39 import gov.nasa.jpf.vm.ThreadInfo;
40 import gov.nasa.jpf.vm.VM;
41 import gov.nasa.jpf.vm.bytecode.FieldInstruction;
42 import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction;
43 import gov.nasa.jpf.vm.bytecode.InstanceInvokeInstruction;
44 import gov.nasa.jpf.vm.bytecode.InstructionInterface;
45 import gov.nasa.jpf.vm.bytecode.InvokeInstruction;
46 import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
47 import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
48 import gov.nasa.jpf.vm.bytecode.ReturnValueInstruction;
49 import gov.nasa.jpf.vm.bytecode.WriteInstruction;
50 import java.io.PrintWriter;
53 * trace where nulls come from - which is either a GETFIELD/STATIC, an
54 * JVMInvokeInstruction, an LocalVariableInstruction or a missing init.
56 * Record/accumulate the causes in an attribute and use the attribute
59 public class NullTracker extends ListenerAdapter {
61 public static abstract class NullSource {
62 protected InstructionInterface insn;
63 protected ThreadInfo ti;
64 protected ElementInfo ei;
66 protected NullSource cause;
68 NullSource (ThreadInfo ti, InstructionInterface insn, ElementInfo ei){
74 public void setCause (NullSource cause){
78 abstract void printOn (PrintWriter pw);
80 void printInsnOn (PrintWriter pw){
81 pw.printf(" instruction: [%04x] %s\n", insn.getPosition(), insn.toString());
84 void printThreadInfoOn (PrintWriter pw){
85 pw.println(" executed by: " + ti.getName() + " (id=" + ti.getId() + ")");
88 void printMethodInfoOn (PrintWriter pw, String msg, InstructionInterface instruction){
89 MethodInfo mi = instruction.getMethodInfo();
90 ClassInfo ci = mi.getClassInfo();
91 pw.println( msg + ci.getName() + '.' + mi.getLongName() + " (" + instruction.getFilePos() + ')');
94 void printCauseOn (PrintWriter pw){
96 pw.println("set by: ");
103 public static class LocalSource extends NullSource {
104 protected LocalVarInfo local;
106 public LocalSource (ThreadInfo ti, LocalVariableInstruction insn, LocalVarInfo local){
107 super(ti, insn, null);
112 void printOn (PrintWriter pw){
115 pw.println(" for local: " + local.getName());
117 pw.println(" for local: #" + ((LocalVariableInstruction)insn).getLocalVariableSlot());
119 printMethodInfoOn(pw, " in method: ", insn);
120 printThreadInfoOn(pw);
126 public static class FieldSource extends NullSource {
127 public FieldSource (ThreadInfo ti, FieldInstruction insn, ElementInfo ei){
132 void printOn (PrintWriter pw){
133 FieldInfo fi = ((FieldInstruction)insn).getFieldInfo();
134 MethodInfo mi = insn.getMethodInfo();
137 pw.println(" for field: " + fi.getFullName());
138 printMethodInfoOn(pw, " in method: ", insn);
139 printThreadInfoOn(pw);
145 public static class MethodSource extends NullSource {
146 InvokeInstruction call;
148 public MethodSource (ThreadInfo ti, InstructionInterface returnInsn, InvokeInstruction call, ElementInfo ei){
149 super(ti,returnInsn,ei);
154 void printOn (PrintWriter pw){
156 printMethodInfoOn(pw, " of method: ", insn);
159 pw.println(" for object: " + ei);
161 printMethodInfoOn(pw, " called by: ", call);
162 printThreadInfoOn(pw);
168 public static class CtorSource extends MethodSource {
169 public CtorSource (ThreadInfo ti, Instruction returnInsn, InvokeInstruction call, ElementInfo ei){
170 super(ti,returnInsn,call, ei);
174 void printOn (PrintWriter pw){
175 printMethodInfoOn(pw, " missing init: ", insn);
178 pw.println(" for object: " + ei);
180 printMethodInfoOn(pw, " called by: ", call);
181 printThreadInfoOn(pw);
187 //---------------------------------------------------------------------------------
189 protected NullSource nullSource;
191 public NullTracker (Config config, JPF jpf){
192 jpf.addPublisherExtension(ConsolePublisher.class, this);
195 protected void checkCtorSourcePre (ThreadInfo ti, ReturnInstruction insn){
196 MethodInfo mi = insn.getMethodInfo();
198 StackFrame callerFrame = null;
199 InvokeInstruction call = null;
200 ElementInfo ei = ti.getThisElementInfo();
201 ClassInfo ci = ei.getClassInfo();
202 int nInstance = ci.getNumberOfDeclaredInstanceFields();
204 for (int i = 0; i < nInstance; i++) {
205 FieldInfo fi = ci.getDeclaredInstanceField(i);
206 if (fi.isReference()) {
207 int ref = ei.getReferenceField(fi);
208 if (ref == MJIEnv.NULL) {
209 ei = ei.getModifiableInstance(); // why do we need this in a ctor?
211 callerFrame = ti.getCallerStackFrame();
212 call = (InvokeInstruction) callerFrame.getPC();
214 NullSource attr = new CtorSource(ti, insn, call, ti.getThisElementInfo());
215 ei.setFieldAttr(fi, attr);
222 protected void checkFieldSourcePre (ThreadInfo ti, WriteInstruction put){
223 FieldInfo fi = put.getFieldInfo();
224 if (fi.isReference()) {
225 StackFrame frame = ti.getTopFrame();
226 int valSlot = put.getValueSlot(frame);
227 int ref = frame.getSlot(valSlot);
229 if (ref == MJIEnv.NULL) { // field will be set to null
230 ElementInfo ei = put.getElementInfo(ti);
231 NullSource attr = new FieldSource(ti, (FieldInstruction)put, ei);
233 NullSource cause = frame.getSlotAttr(valSlot, NullSource.class);
235 attr.setCause(cause);
236 frame.replaceSlotAttr(valSlot, cause, attr);
238 frame.addSlotAttr(valSlot, attr);
244 protected void checkMethodSourcePre (ThreadInfo ti, ReturnValueInstruction aret){
245 StackFrame frame = ti.getTopFrame();
246 int valSlot = aret.getValueSlot(frame);
247 int ref = frame.getSlot(valSlot);
249 if (ref == MJIEnv.NULL) {
250 StackFrame callerFrame = ti.getCallerStackFrame();
251 InvokeInstruction call = (InvokeInstruction) callerFrame.getPC();
252 NullSource attr = new MethodSource(ti, aret, call, ti.getThisElementInfo());
254 NullSource cause = frame.getSlotAttr(valSlot, NullSource.class);
256 attr.setCause(cause);
257 frame.replaceSlotAttr(valSlot,cause, attr);
259 frame.addSlotAttr(valSlot,attr);
265 public void executeInstruction (VM vm, ThreadInfo ti, Instruction insn) {
267 if (insn instanceof ARETURN){
268 checkMethodSourcePre( ti, (ARETURN)insn);
270 } else if (insn instanceof PUTFIELD || insn instanceof PUTSTATIC){
271 checkFieldSourcePre( ti, (WriteInstruction) insn);
273 } else if (insn instanceof RETURN){
274 checkCtorSourcePre(ti, (RETURN) insn);
279 protected void checkLocalSourcePost (ThreadInfo ti, LocalVariableInstruction insn){
280 int slotIdx = insn.getLocalVariableSlot();
281 StackFrame frame = ti.getTopFrame();
282 int ref = frame.getSlot(slotIdx);
283 if (ref == MJIEnv.NULL) {
284 LocalVarInfo lv = insn.getLocalVarInfo();
285 NullSource attr = new LocalSource(ti, insn, lv);
287 NullSource cause = frame.getSlotAttr(slotIdx, NullSource.class);
289 attr.setCause(cause);
290 frame.replaceSlotAttr(slotIdx, cause, attr);
292 frame.addSlotAttr(slotIdx, attr);
298 public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction insn){
300 // we need to do LocalVariableInstruction post exec since it did overwrite the attr if it had an immediate operand
301 if (insn instanceof ASTORE) {
302 checkLocalSourcePost( ti, (LocalVariableInstruction)insn);
307 public void exceptionThrown(VM vm, ThreadInfo ti, ElementInfo thrownException) {
308 if (thrownException.instanceOf("Ljava/lang/NullPointerException;")){
309 StackFrame frame = ti.getTopFrame();
310 Instruction insn = ti.getPC();
312 if (insn instanceof InstanceFieldInstruction){ // field access on null object
313 int objSlot = ((InstanceFieldInstruction)insn).getObjectSlot(frame);
314 NullSource attr = frame.getSlotAttr( objSlot,NullSource.class);
319 } else if (insn instanceof InstanceInvokeInstruction) { // call on a null object
320 int objSlot = ((InstanceInvokeInstruction)insn).getObjectSlot(frame);
321 NullSource attr = frame.getSlotAttr( objSlot, NullSource.class);
331 public void publishPropertyViolation (Publisher publisher) {
332 if (nullSource != null){ // otherwise we don't have anything to report
333 PrintWriter pw = publisher.getOut();
334 publisher.publishTopicStart("NullTracker " + publisher.getLastErrorId());
336 pw.println("null value set by: ");
337 nullSource.printOn(pw);