Initial import
[jpf-core.git] / src / main / gov / nasa / jpf / listener / NullTracker.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
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
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
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.
17  */
18
19 package gov.nasa.jpf.listener;
20
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;
51
52 /**
53  * trace where nulls come from - which is either a GETFIELD/STATIC, an
54  * JVMInvokeInstruction, an LocalVariableInstruction or a missing init.
55  * 
56  * Record/accumulate the causes in an attribute and use the attribute 
57  * to explain NPEs
58  */
59 public class NullTracker extends ListenerAdapter {
60
61   public static abstract class NullSource {
62     protected InstructionInterface insn;
63     protected ThreadInfo ti;
64     protected ElementInfo ei;
65     
66     protected NullSource cause;
67     
68     NullSource (ThreadInfo ti, InstructionInterface insn, ElementInfo ei){
69       this.ti = ti;
70       this.insn = insn;
71       this.ei = ei;
72     }
73     
74     public void setCause (NullSource cause){
75       this.cause = cause;
76     }
77     
78     abstract void printOn (PrintWriter pw);
79     
80     void printInsnOn (PrintWriter pw){
81       pw.printf("    instruction: [%04x] %s\n", insn.getPosition(), insn.toString());
82     }
83         
84     void printThreadInfoOn (PrintWriter pw){
85       pw.println("    executed by: " + ti.getName() + " (id=" + ti.getId() + ")");
86     }
87     
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() + ')');
92     }
93     
94     void printCauseOn (PrintWriter pw){
95       if (cause != null){
96         pw.println("set by: ");
97         cause.printOn(pw);
98       }
99     }
100   }
101   
102   
103   public static class LocalSource extends NullSource {
104     protected LocalVarInfo local;
105     
106     public LocalSource (ThreadInfo ti, LocalVariableInstruction insn, LocalVarInfo local){
107       super(ti, insn, null);
108       this.local = local;
109     }
110     
111     @Override
112     void printOn (PrintWriter pw){
113       printInsnOn(pw);
114       if (local != null){
115         pw.println("      for local: " + local.getName());
116       } else {
117         pw.println("     for local: #" + ((LocalVariableInstruction)insn).getLocalVariableSlot());
118       }
119       printMethodInfoOn(pw, "      in method: ", insn);
120       printThreadInfoOn(pw);
121       
122       printCauseOn(pw);
123     }
124   }
125   
126   public static class FieldSource extends NullSource {
127     public FieldSource (ThreadInfo ti, FieldInstruction insn, ElementInfo ei){
128       super(ti,insn,ei);
129     }
130     
131     @Override
132     void printOn (PrintWriter pw){
133       FieldInfo fi = ((FieldInstruction)insn).getFieldInfo();
134       MethodInfo mi = insn.getMethodInfo();
135             
136       printInsnOn(pw);
137       pw.println("      for field: " + fi.getFullName());
138       printMethodInfoOn(pw, "      in method: ", insn);
139       printThreadInfoOn(pw);
140       
141       printCauseOn(pw);
142     }
143   }
144
145   public static class MethodSource extends NullSource {
146     InvokeInstruction call;
147     
148     public MethodSource (ThreadInfo ti, InstructionInterface returnInsn, InvokeInstruction call, ElementInfo ei){
149       super(ti,returnInsn,ei);
150       this.call = call;
151     }
152     
153     @Override
154     void printOn (PrintWriter pw){            
155       printInsnOn(pw);
156       printMethodInfoOn(pw, "      of method: ", insn);
157       
158       if (ei != null){
159         pw.println("     for object: " + ei);
160       }
161       printMethodInfoOn(pw, "      called by: ", call);
162       printThreadInfoOn(pw);
163       
164       printCauseOn(pw);
165     }    
166   }
167   
168   public static class CtorSource extends MethodSource {
169     public CtorSource (ThreadInfo ti, Instruction returnInsn, InvokeInstruction call, ElementInfo ei){
170       super(ti,returnInsn,call, ei);
171     }
172     
173     @Override
174     void printOn (PrintWriter pw){ 
175       printMethodInfoOn(pw, "   missing init: ", insn);
176       
177       if (ei != null){
178         pw.println("     for object: " + ei);
179       }
180       printMethodInfoOn(pw, "      called by: ", call);
181       printThreadInfoOn(pw);
182       
183       printCauseOn(pw);
184     }    
185   }
186
187   //---------------------------------------------------------------------------------
188   
189   protected NullSource nullSource;
190   
191   public NullTracker (Config config, JPF jpf){
192     jpf.addPublisherExtension(ConsolePublisher.class, this);
193   }
194   
195   protected void checkCtorSourcePre (ThreadInfo ti, ReturnInstruction insn){
196     MethodInfo mi = insn.getMethodInfo();
197     if (mi.isCtor()) {
198       StackFrame callerFrame = null;
199       InvokeInstruction call = null;
200       ElementInfo ei = ti.getThisElementInfo();
201       ClassInfo ci = ei.getClassInfo();
202       int nInstance = ci.getNumberOfDeclaredInstanceFields();
203       
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?
210             if (call == null) {
211               callerFrame = ti.getCallerStackFrame();
212               call = (InvokeInstruction) callerFrame.getPC();
213             }
214             NullSource attr = new CtorSource(ti, insn, call, ti.getThisElementInfo());
215             ei.setFieldAttr(fi, attr);
216           }
217         }
218       }
219     }
220   }
221   
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);
228
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);
232
233         NullSource cause = frame.getSlotAttr(valSlot, NullSource.class);
234         if (cause != null) {
235           attr.setCause(cause);
236           frame.replaceSlotAttr(valSlot, cause, attr);
237         } else {
238           frame.addSlotAttr(valSlot, attr);
239         }
240       }
241     }    
242   }
243   
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);
248     
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());
253
254       NullSource cause = frame.getSlotAttr(valSlot, NullSource.class);
255       if (cause != null) {
256         attr.setCause(cause);
257         frame.replaceSlotAttr(valSlot,cause, attr);
258       } else {
259         frame.addSlotAttr(valSlot,attr);
260       }
261     }
262   }
263   
264   @Override
265   public void executeInstruction (VM vm, ThreadInfo ti, Instruction insn) {
266     
267     if (insn instanceof ARETURN){
268       checkMethodSourcePre( ti, (ARETURN)insn);
269       
270     } else if (insn instanceof PUTFIELD || insn instanceof PUTSTATIC){
271       checkFieldSourcePre( ti, (WriteInstruction) insn);
272       
273     } else if (insn instanceof RETURN){
274       checkCtorSourcePre(ti, (RETURN) insn);
275     }
276   }
277
278   
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);
286
287       NullSource cause = frame.getSlotAttr(slotIdx, NullSource.class);
288       if (cause != null) {
289         attr.setCause(cause);
290         frame.replaceSlotAttr(slotIdx, cause, attr);
291       } else {
292         frame.addSlotAttr(slotIdx, attr);
293       }
294     }
295   }
296   
297   @Override
298   public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction insn){
299     
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);
303     }
304   }
305     
306   @Override
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();
311       
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);
315         if (attr != null) {
316           nullSource = attr;
317         }
318         
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);
322         if (attr != null) {
323           nullSource = attr;
324         }
325       }
326     }
327   }
328
329   
330   @Override
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());
335
336       pw.println("null value set by: ");
337       nullSource.printOn(pw);
338     }
339   }
340 }