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.
18 package gov.nasa.jpf.listener;
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.ListenerAdapter;
23 import gov.nasa.jpf.jvm.bytecode.InstanceInvocation;
24 import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
25 import gov.nasa.jpf.jvm.bytecode.JVMReturnInstruction;
26 import gov.nasa.jpf.report.ConsolePublisher;
27 import gov.nasa.jpf.report.Publisher;
28 import gov.nasa.jpf.search.Search;
29 import gov.nasa.jpf.util.StringSetMatcher;
30 import gov.nasa.jpf.vm.ElementInfo;
31 import gov.nasa.jpf.vm.Instruction;
32 import gov.nasa.jpf.vm.MJIEnv;
33 import gov.nasa.jpf.vm.VM;
34 import gov.nasa.jpf.vm.MethodInfo;
35 import gov.nasa.jpf.vm.StackFrame;
36 import gov.nasa.jpf.vm.ThreadInfo;
37 import gov.nasa.jpf.vm.Types;
39 import java.io.PrintWriter;
40 import java.util.HashMap;
44 * analyzes call/execute sequences of methods
45 * closely modeled after the DeadlockAnalyzer, i.e. keeps it's own
46 * log and doesn't require full instruction trace
48 * <2do> this needs to be refactored with DeadlockAnalyzer - the whole
49 * trace mgnt (except of the printing) can be made generic
51 public class MethodAnalyzer extends ListenerAdapter {
53 enum OpType { CALL ("> "), // invokeX breaks transition (e.g. blocked sync)
54 EXECUTE (" - "), // method entered method after transition break
55 CALL_EXECUTE (">- "), // call & enter within same transition
56 RETURN (" <"), // method returned
57 EXEC_RETURN (" -<"), // enter & return in consecutive ops
58 CALL_EXEC_RETURN (">-<"); // call & enter & return in consecutive ops
60 OpType (String code){ this.code = code; }
63 static class MethodOp {
68 Instruction insn; // the caller
69 MethodInfo mi; // the callee
72 // this is used to keep our own trace
74 MethodOp prevTransition;
75 MethodOp p; // prev during execution
77 MethodOp (OpType type, MethodInfo mi, ThreadInfo ti, ElementInfo ei, int stackDepth){
82 this.stackDepth = stackDepth;
85 MethodOp clone (OpType newType){
86 MethodOp op = new MethodOp(newType, mi, ti, ei, stackDepth);
91 boolean isMethodEnter() {
92 return (type == OpType.CALL_EXECUTE) || (type == OpType.EXECUTE);
95 boolean isSameMethod(MethodOp op) {
96 return (mi == op.mi) && (ti == op.ti) && (ei == op.ei) && (stackDepth == op.stackDepth);
99 void printOn(PrintWriter pw, MethodAnalyzer analyzer) {
100 pw.print(ti.getId());
106 if (analyzer.showDepth){
107 for (int i = 0; i < stackDepth; i++) {
114 if (ei.getClassInfo() != mi.getClassInfo()){ // method is in superclass
115 pw.print(mi.getClassName());
119 } else { // method is in concrete object class
123 pw.print(mi.getClassName());
128 pw.print(Types.getDequalifiedMethodSignature(mi.getUniqueName()));
132 public String toString() {
133 return "Op {" + ti.getName() + ',' + type.code +
134 ',' + mi.getFullName() + ',' + ei + '}';
140 StringSetMatcher includes = null; // means all
141 StringSetMatcher excludes = null; // means none
147 boolean showTransition;
148 boolean showCompleted;
150 // execution environment
157 // this is used to keep our own trace
159 MethodOp lastTransition;
160 boolean isFirstTransition = true;
162 // this is set after we call revertAndFlatten during reporting
163 // (we can't call revertAndFlatten twice since it is destructive, but
164 // we might have to report several times in case we have several publishers)
165 MethodOp firstOp = null;
167 // for HeuristicSearches. Ok, that's braindead but at least no need for cloning
168 HashMap<Integer,MethodOp> storedTransition = new HashMap<Integer,MethodOp>();
171 public MethodAnalyzer (Config config, JPF jpf){
172 jpf.addPublisherExtension(ConsolePublisher.class, this);
174 maxHistory = config.getInt("method.max_history", Integer.MAX_VALUE);
175 format = config.getString("method.format", "raw");
176 skipInit = config.getBoolean("method.skip_init", true);
177 showDepth = config.getBoolean("method.show_depth", false);
178 showTransition = config.getBoolean("method.show_transition", false);
180 includes = StringSetMatcher.getNonEmpty(config.getStringArray("method.include"));
181 excludes = StringSetMatcher.getNonEmpty(config.getStringArray("method.exclude"));
184 search = jpf.getSearch();
188 void addOp (VM vm, OpType opType, MethodInfo mi, ThreadInfo ti, ElementInfo ei, int stackDepth){
189 if (!(skipInit && isFirstTransition)) {
190 MethodOp op = new MethodOp(opType, mi, ti, ei, stackDepth);
200 boolean isAnalyzedMethod (MethodInfo mi){
202 String mthName = mi.getFullName();
203 return StringSetMatcher.isMatch(mthName, includes, excludes);
209 void printOn (PrintWriter pw) {
210 MethodOp start = firstOp;
211 int lastStateId = Integer.MIN_VALUE;
212 int transition = skipInit ? 1 : 0;
213 int lastTid = start.ti.getId();
215 for (MethodOp op = start; op != null; op = op.p) {
217 if (showTransition) {
218 if (op.stateId != lastStateId) {
219 lastStateId = op.stateId;
220 pw.print("------------------------------------------ #");
221 pw.println(transition++);
224 int tid = op.ti.getId();
225 if (tid != lastTid) {
227 pw.println("------------------------------------------");
231 op.printOn(pw, this);
236 // warning - this rotates pointers in situ, i.e. destroys the original structure
237 MethodOp revertAndFlatten (MethodOp start) {
239 MethodOp last = null;
240 MethodOp prevTransition = start.prevTransition;
242 for (MethodOp op = start; op != null;) {
247 if (prevTransition == null) {
252 prevTransition = op.prevTransition;
263 //--- SearchListener interface
264 // <2do> this is the same as DeadlockAnalyzer, except of xxOp type -> refactor
266 public void stateAdvanced (Search search){
268 if (search.isNewState() && (lastOp != null)) {
269 int stateId = search.getStateId();
271 for (MethodOp op=lastOp; op != null; op=op.p) {
272 op.stateId = stateId;
275 lastOp.prevTransition = lastTransition;
276 lastTransition = lastOp;
280 isFirstTransition = false;
284 public void stateBacktracked (Search search){
285 int stateId = search.getStateId();
286 while ((lastTransition != null) && (lastTransition.stateId > stateId)){
287 lastTransition = lastTransition.prevTransition;
293 public void stateStored (Search search) {
294 // always called after stateAdvanced
295 storedTransition.put(search.getStateId(), lastTransition);
299 public void stateRestored (Search search) {
300 int stateId = search.getStateId();
301 MethodOp op = storedTransition.get(stateId);
304 storedTransition.remove(stateId); // not strictly required, but we don't come back
309 //--- VMlistener interface
311 public void instructionExecuted (VM vm, ThreadInfo thread, Instruction nextInsn, Instruction executedInsn) {
314 ElementInfo ei = null;
316 if (executedInsn instanceof JVMInvokeInstruction) {
317 JVMInvokeInstruction call = (JVMInvokeInstruction)executedInsn;
319 mi = call.getInvokedMethod(ti);
321 if (isAnalyzedMethod(mi)) {
324 // check if this was actually executed, or is a blocked sync call
325 if (ti.getNextPC() == call) { // re-executed -> blocked or overlayed
329 if (ti.isFirstStepInsn()) {
330 type = OpType.EXECUTE;
332 type = OpType.CALL_EXECUTE;
336 if (call instanceof InstanceInvocation) {
337 ei = ((InstanceInvocation)call).getThisElementInfo(ti);
340 addOp(vm,type,mi,ti,ei, ti.getStackDepth());
343 } else if (executedInsn instanceof JVMReturnInstruction) {
344 JVMReturnInstruction ret = (JVMReturnInstruction)executedInsn;
346 StackFrame frame = ret.getReturnFrame();
347 mi = frame.getMethodInfo();
349 if (isAnalyzedMethod(mi)) {
350 if (!mi.isStatic()) {
351 int ref = frame.getThis();
352 if (ref != MJIEnv.NULL) {
353 ei = ti.getElementInfo(ref);
357 addOp(vm,OpType.RETURN,mi,ti,ei, ti.getStackDepth()+1); // postExec-> frame already popped
362 //--- the PubisherExtension part
364 public void publishPropertyViolation (Publisher publisher) {
366 if (firstOp == null && lastTransition != null){ // do this just once
367 firstOp = revertAndFlatten(lastTransition);
370 if (firstOp == null){
374 PrintWriter pw = publisher.getOut();
375 publisher.publishTopicStart("method ops " + publisher.getLastErrorId());