//
// Copyright (C) 2010 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 java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.JPF;
import gov.nasa.jpf.vm.ElementInfo;
import gov.nasa.jpf.vm.MethodInfo;
import gov.nasa.jpf.vm.ThreadInfo;
/**
* this is a specialized MethodAnalyzer that looks for overlapping method
* calls on the same object from different threads.
*/
public class OverlappingMethodAnalyzer extends MethodAnalyzer {
public OverlappingMethodAnalyzer (Config config, JPF jpf){
super(config,jpf);
}
MethodOp getReturnOp (MethodOp op, boolean withinSameThread){
MethodInfo mi = op.mi;
// int stateId = op.stateId;
int stackDepth = op.stackDepth;
ElementInfo ei = op.ei;
ThreadInfo ti = op.ti;
for (MethodOp o = op.p; o != null; o = o.p){
if (withinSameThread && o.ti != ti){
break;
}
if ((o.mi == mi) && (o.ti == ti) && (o.stackDepth == stackDepth) && (o.ei == ei)){
return o;
}
}
return null;
}
// check if there is an open exec from another thread for the same ElementInfo
boolean isOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
ThreadInfo ti = op.ti;
ElementInfo ei = op.ei;
for (Map.Entry<ThreadInfo, Deque<MethodOp>> e : openExecs.entrySet()) {
if (e.getKey() != ti) {
Deque<MethodOp> s = e.getValue();
for (Iterator<MethodOp> it = s.descendingIterator(); it.hasNext();) {
MethodOp o = it.next();
if (o.ei == ei) {
return true;
}
}
}
}
return false;
}
// clean up (if necessary) - both RETURNS and exceptions
void cleanUpOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
ThreadInfo ti = op.ti;
int stackDepth = op.stackDepth;
Deque<MethodOp> stack = openExecs.get(ti);
if (stack != null && !stack.isEmpty()) {
for (MethodOp o = stack.peek(); o != null && o.stackDepth >= stackDepth; o = stack.peek()) {
stack.pop();
}
}
}
void addOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
ThreadInfo ti = op.ti;
Deque<MethodOp> stack = openExecs.get(ti);
if (stack == null){
stack = new ArrayDeque<MethodOp>();
stack.push(op);
openExecs.put(ti, stack);
} else {
stack.push(op);
}
}
void printOn (PrintWriter pw) {
MethodOp start = firstOp;
HashMap<ThreadInfo,Deque<MethodOp>> openExecs = new HashMap<ThreadInfo,Deque<MethodOp>>();
int lastStateId = Integer.MIN_VALUE;
int transition = skipInit ? 1 : 0;
int lastTid = start.ti.getId();
for (MethodOp op = start; op != null; op = op.p) {
if (showTransition) {
if (op.stateId != lastStateId) {
lastStateId = op.stateId;
pw.print("------------------------------------------ #");
pw.println(transition++);
}
} else {
int tid = op.ti.getId();
if (tid != lastTid) {
lastTid = tid;
pw.println("------------------------------------------");
}
}
cleanUpOpenExec(openExecs, op);
if (op.isMethodEnter()) { // EXEC or CALL_EXEC
MethodOp retOp = getReturnOp(op, true);
if (retOp != null) { // completed, skip
if (!isOpenExec(openExecs, op)) {
op = retOp;
continue;
}
} else { // this is an open method exec, record it
addOpenExec(openExecs, op);
}
}
op = consolidateOp(op);
op.printOn(pw, this);
pw.println();
}
}
@SuppressWarnings("incomplete-switch")
MethodOp consolidateOp (MethodOp op){
for (MethodOp o = op.p; o != null; o = o.p){
if (showTransition && (o.stateId != op.stateId)){
break;
}
if (o.isSameMethod(op)){
switch (o.type) {
case RETURN:
switch (op.type){
case CALL_EXECUTE:
op = o.clone(OpType.CALL_EXEC_RETURN); break;
case EXECUTE:
op = o.clone(OpType.EXEC_RETURN); break;
}
break;
case EXEC_RETURN:
switch (op.type){
case CALL:
op = o.clone(OpType.CALL_EXEC_RETURN); break;
}
break;
case CALL_EXECUTE: // simple loop
switch (op.type){
case CALL_EXEC_RETURN:
op = o;
}
break;
}
} else {
break;
}
}
return op;
}
}