/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * (C) Copyright IBM Corporation 2006-2010. */ package x10.wala.translator; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import polyglot.main.Report; import x10.compiler.ws.util.WSTransformationContent; import x10.compiler.ws.util.WSTransformationContent.MethodAttribute; import x10.wala.classLoader.AsyncCallSiteReference; import com.ibm.wala.cast.loader.AstMethod; import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; import com.ibm.wala.ipa.callgraph.impl.Everywhere; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.types.Selector; import com.ibm.wala.util.graph.traverse.NumberedDFSDiscoverTimeIterator; /** * @author Haichuan * Analyze the call graph from wala and identify all transformation targets */ public class X10WSCallGraphAnalyzer { public static final void wsReport(int level, String message){ if(Report.workstealing >= level){ Report.reporter.report(level, message); } } public static final boolean wsShouldReport(int level){ return Report.workstealing >= level; } static{ // TODO: stop using static Report // Report is being moved to Reporter to avoid Report's static data // Report.topics.add just adds this string to be displayed in the // help text. Skipping the following until this is resolved. // Report.topics.add(WS_TOPIC); wsReport(1, "[WS_CallGraph]X10WSCallGraphAnalyzer started..."); } protected CallGraph callGraph; protected IClassHierarchy classHC; public X10WSCallGraphAnalyzer(CallGraph callGraph){ this.callGraph = callGraph; classHC = callGraph.getClassHierarchy(); } /** * Identify all concurrent methods, and * Use DFS to traverse all methods that directly or indirectly call the concurrent methods * This method doesn't use class hierarchy information. * @return */ public WSTransformationContent simpleAnalyze(){ WSTransformationContent result = new WSTransformationContent(); //identify all concurrent methods by checking the async call site HashSet<CGNode> conMethodNodes = new HashSet<CGNode>(); for(CGNode cgn : callGraph){ if(cgn.getMethod() == null){ continue; } for (Iterator<CallSiteReference> sites = cgn.iterateCallSites(); sites.hasNext();) { CallSiteReference site = (CallSiteReference) sites.next(); if(site instanceof AsyncCallSiteReference){ conMethodNodes.add(cgn); break; //no need identify again; } } } //now build a reverse DFS iterator; Iterator<CGNode> dfsI = new NumberedDFSDiscoverTimeIterator<CGNode>(callGraph, conMethodNodes.iterator()){ private static final long serialVersionUID = -8848061109177832957L; protected Iterator<? extends CGNode> getConnected(CGNode n) { return callGraph.getPredNodes(n); //reverse traverse; } }; //now add all reachable nodes, but filter out fake root and async activity HashSet<CGNode> targetMethodNodes = new HashSet<CGNode>(); while(dfsI.hasNext()){ CGNode cgn = dfsI.next(); if(cgn != callGraph.getFakeRootNode() && cgn.getMethod() != null && !cgn.getMethod().getSignature().startsWith("A<activity")){ targetMethodNodes.add(cgn); } } //Iterate all target node to do the following actions //1. Add each node's method as target method (body & def) transformation //2. Add each call sites to the target method as concurrent call site //3. Add each target method's impacted method as def transformation target for(CGNode targetNode : targetMethodNodes){ AstMethod targetMethod = (AstMethod) targetNode.getMethod(); Position pos = targetMethod.getSourcePosition(); //DEBUG information //show location information //System.out.printf("[TargetMethod]%s\n", targetMethod); //System.out.printf(" url:%s; line:%d; col:%d\n", // pos.getURL(), pos.getFirstLine(), pos.getFirstCol()); //Action 1 String info = conMethodNodes.contains(targetNode) ? "[C]" : "[D]"; info += targetNode.getMethod(); String url = pos.getURL().toString().substring(5); //remove "files" result.addConcurrentMethod(url, pos.getFirstLine(), pos.getFirstCol(), pos.getLastLine(), pos.getLastCol(), info); //Action 2 for(Iterator<CGNode> srcNodesI = callGraph.getPredNodes(targetNode); srcNodesI.hasNext();){ CGNode srcNode = srcNodesI.next(); IMethod method = srcNode.getMethod(); if(!(method instanceof AstMethod)){ continue; //remove the fake root; } AstMethod srcMethod = (AstMethod) method; for(Iterator<CallSiteReference> callSitesI = callGraph.getPossibleSites(srcNode, targetNode); callSitesI.hasNext();){ CallSiteReference callSite = callSitesI.next(); int callPC = callSite.getProgramCounter(); Position callSitePos = srcMethod.debugInfo().getInstructionPosition(callPC); //DEBUG //IR ir = srcNode.getIR(); //System.out.printf("[CallSite]%s\n", callSite); //System.out.printf("[CallInst]%s\n", ir.getInstructions()[callPC]); //System.out.printf(" url:%s; line:%d; col:%d\n", // callSitePos.getURL(), callSitePos.getFirstLine(), callSitePos.getFirstCol()); result.addConcurrentCallSite(callSitePos.getURL().toString().substring(5), callSitePos.getFirstLine(), callSitePos.getFirstCol(), callSitePos.getLastLine(), callSitePos.getLastCol(), callSite.toString()); } } //Action 3, still use try catch. Could remove them try{ Set<IMethod> methods = getAllImpactedMethodsInClassHierarchy(targetMethod); for(IMethod m : methods){ if(!(m instanceof AstMethod)){ System.err.println("[WALA_WS_ERR]Found one non-AST but impacted Method" + m); continue; } Position mPos = ((AstMethod)m).getSourcePosition(); MethodAttribute ma = result.addImpactedMethod(mPos.getURL().toString().substring(5), mPos.getFirstLine(), mPos.getFirstCol(), mPos.getLastLine(), mPos.getLastCol(), "[x]"+ m); //Now check the node's call sites if(ma == null){ continue; //the node is a concurrent method or has been added before //no need further processing } //get the call graph node, if null, dead node, ignore CGNode node = callGraph.getNode(m, Everywhere.EVERYWHERE); if(node == null){ //the method is in dead code, mark it as dead code ma.setDead(true); continue; //the method has never been called in the app. } for(Iterator<CGNode> srcNodesI = callGraph.getPredNodes(node); srcNodesI.hasNext();){ CGNode srcNode = srcNodesI.next(); IMethod method = srcNode.getMethod(); if(!(method instanceof AstMethod)){ continue; //remove the fake root; } AstMethod srcMethod = (AstMethod) method; for(Iterator<CallSiteReference> callSitesI = callGraph.getPossibleSites(srcNode, node); callSitesI.hasNext();){ CallSiteReference callSite = callSitesI.next(); int callPC = callSite.getProgramCounter(); Position callSitePos = srcMethod.debugInfo().getInstructionPosition(callPC); result.addMatchedCallSite(callSitePos.getURL().toString().substring(5), callSitePos.getFirstLine(), callSitePos.getFirstCol(), callSitePos.getLastLine(), callSitePos.getLastCol(), callSite.toString()); } //loop: all call sites } //loop: all src node to node } // loop: all impacted method } //try catch(Exception e){ e.printStackTrace(); } } //finally show call graph //System.out.println(callGraph); return result; } /** * It will check all the related methods in class hierarchy * including * 1) if the method is defined in a interface, all the methods implemented the interface's definition * 2) the method's containing class's super/sub classes's corresponding methods * @param method the source method * @return a set of methods, not including the source method */ protected Set<IMethod> getAllImpactedMethodsInClassHierarchy(IMethod method){ Set<IMethod> result = new HashSet<IMethod>(); IClass methodClass = method.getDeclaringClass(); //the containing class Selector mSelector = method.getSelector(); //used to locate the method //1st place to search it's interface //System.out.println(" --> Scan Interfaces"); Collection<IClass> interfaceClasses = methodClass.getAllImplementedInterfaces(); for(IClass ifc : interfaceClasses){ IMethod m = ifc.getMethod(mSelector); if(m != null){ //System.out.println(" From interface("+ c + ") method:" + m); result.add(m); //Then we need find all the method that implements the interface Set<IClass> classes = classHC.getImplementors(ifc.getReference()); for(IClass ic : classes){ //each class should have the method //System.out.println(" From class"+ic); IMethod im = ic.getMethod(mSelector); if(im != null){ //System.out.println(" Method:" + im); result.add(im); } } } } //2nd place to search, the class tree //locate top class IClass curClass = methodClass; IClass superClass = curClass; while(curClass != null && curClass != classHC.getRootClass()){ superClass = curClass; curClass = curClass.getSuperclass(); } //System.out.println("Scan SuperClass tree" + superClass); Collection<IClass> classes = classHC.computeSubClasses(superClass.getReference()); classes.add(superClass); //add the super class, too; //now scan all classes that contains the method for(IClass c : classes){ IMethod m = c.getMethod(mSelector); if(m != null){ //System.out.println(" From class("+ c + ") method:" + m); result.add(m); } } //finally, we removed the original method itself result.remove(method); return result; } }