Package tbrugz.rtcoverage

Source Code of tbrugz.rtcoverage.JDIEventProcessor

package tbrugz.rtcoverage;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sun.jdi.ArrayReference;
import com.sun.jdi.ClassLoaderReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.Field;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StringReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.ClassUnloadEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventIterator;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.ExceptionEvent;
import com.sun.jdi.event.ThreadDeathEvent;
import com.sun.jdi.event.ThreadStartEvent;
import com.sun.jdi.event.VMDisconnectEvent;

public class JDIEventProcessor extends Thread {

  static Log log = LogFactory.getLog(JDIEventProcessor.class);
  static Log logClazz = LogFactory.getLog(JDIEventProcessor.class.getName()+".class");
  static Log logThread = LogFactory.getLog(JDIEventProcessor.class.getName()+".thread");
  static Log logException = LogFactory.getLog(JDIEventProcessor.class.getName()+".ex");
  static Log logAnalysis = LogFactory.getLog(JDIEventProcessor.class.getName()+".analysis");

  class ShowClassesLoaded extends Thread {
    @Override
    public void run() {
      showJarsUsed();
    }
  }
 
  //final Set<String> classPathJarsTracked = new TreeSet<String>();
    //final Set<String> classesLoaded = new TreeSet<String>();
    final Set<String> classesShadowed = new TreeSet<String>();
    final Set<String> jarsLoaded = new TreeSet<String>();
    final Set<String> jarsInExistence = new TreeSet<String>();
    final Set<String> urlsNotFound = new TreeSet<String>();
    final Map<String, Set<String>> jarContentsCache = new HashMap<String, Set<String>>();
    final Set<String> classloadersWithNoGetURLsMethod = new TreeSet<String>();
    //final Set<String> classloadersWithNoGetParentMethod = new TreeSet<String>();
    final Map<String, String> classesLoadedClassLoaders = new TreeMap<String, String>();

    final Map<String, String> parentClassLoaders = new HashMap<String, String>();
    final Map<String, String> classLoaderNames = new HashMap<String, String>();
   
    //Set<String> jarsWithManifestTracked = new TreeSet<String>();
 
  EventQueue eventQueue;
    //FileFinder ff = new FileFinder(null, null);

    long runningTime = 0;
    long eventTime = 0;
 
    public JDIEventProcessor(EventQueue eventQueue) {
        super();
        this.eventQueue = eventQueue;
        start();
    }
   
    @Override
    public void run() {
        long initTime = System.currentTimeMillis();
        try {
         
          //so if the program is interrupter it still shows (hopefully) the classes/jars used
          Thread shutdownHook = new ShowClassesLoaded();
          Runtime.getRuntime().addShutdownHook(shutdownHook);
         
            boolean stop = false;
           
            do {
                EventSet eventSet = eventQueue.remove();
                long eventIniTime = System.currentTimeMillis();
                //int resumeVMtimes = 0;
                for(EventIterator i = eventSet.eventIterator(); i.hasNext(); ) {
                    Event evt = i.nextEvent();
                    if(evt instanceof ClassPrepareEvent) {
                        ClassPrepareEvent cpe = (ClassPrepareEvent) evt;
                        ReferenceType refType = cpe.referenceType();
                        logClazz.debug("Loading: " + refType.name() + " [cl:" + refType.classLoader()+"]");
                        loadClassDebug(refType, cpe.thread());
                    } else if(evt instanceof ClassUnloadEvent) {
                      ClassUnloadEvent cue = (ClassUnloadEvent) evt;
                      logClazz.debug("Unloading: " + cue.className());
                    } else if(evt instanceof ThreadStartEvent) {
                      ThreadStartEvent tse = (ThreadStartEvent) evt;
                      threadEventDebug(tse.thread(), "Thread Start");
                      //logThread.debug("Thread Start: " + tse.thread().name());
                    } else if(evt instanceof ThreadDeathEvent) {
                      ThreadDeathEvent tde = (ThreadDeathEvent) evt;
                      threadEventDebug(tde.thread(), "Thread Death");
                      //resumeVMtimes++;
                      //tde.virtualMachine().resume();
                      //logThread.debug("Thread Death: " + tde.thread().name());
                      //SuspendVMTimed svt = new SuspendVMTimed(tde.virtualMachine(), 1000);
                    } else if(evt instanceof ExceptionEvent) {
                      ExceptionEvent exe = (ExceptionEvent) evt;
                      exceptionEventDebug(exe);
                    } else if(evt instanceof VMDisconnectEvent) {
                      log.debug("VMDisconnectEvent!");
                      stop = true;
                    }
                }
                eventTime += (System.currentTimeMillis() - eventIniTime);
                //for(int i=0;i<resumeVMtimes;i++) {
                //  eventQueue.virtualMachine().resume();
                //}
              //resumeVMtimes = 0;
                eventSet.resume();
                //eventQueue.virtualMachine().resume();
            } while(!stop);
        }
        catch(Throwable oops) {
            oops.printStackTrace();
        }
        finally {
          runningTime = (System.currentTimeMillis()-initTime);
          log.info("running time: "+runningTime+"ms ; event treatment time: "+eventTime+
              " ; ratio: "+((double)eventTime/(double)runningTime));
          //showJarsUsed();
        }
    }
   
    void loadClassDebug(ReferenceType rt, ThreadReference thr) {
      ClassLoaderReference clr = rt.classLoader();
      if(clr==null) return; //don't deal with bootstrap classloader
     
      try {
      String classLoaderClassName = clr.referenceType().name();
      IntegerValue vHashCode = (IntegerValue) simpleInvokeVMMethod(thr, clr, "hashCode");
      String iHashCode = Integer.toHexString(vHashCode.value());

      classJarsDebug(rt, thr, clr, classLoaderClassName, iHashCode);
      classloaderGetParentDebug(thr, clr, classLoaderClassName, iHashCode);
      } catch(Exception e) {
      e.printStackTrace();
      }
    }
   
    void classJarsDebug(ReferenceType rt, ThreadReference thr, ClassLoaderReference clr, String classLoaderClassName, String iHashCode) {
      try {
      Value vURLs = null;
      String pathFound = null;
      if(classloadersWithNoGetURLsMethod.contains(classLoaderClassName)) { return; }
      try {
        vURLs = simpleInvokeVMMethod(thr, clr, "getURLs");
      }
      catch(NoSuchMethodException nsme) {
        logClazz.warn(nsme.getMessage());
        classloadersWithNoGetURLsMethod.add(classLoaderClassName);
      }
      if(vURLs==null) { return; }
      //logClazz.debug("   CL-URL:"+vURLs.toString()+"/"+vURLs.type());
      ArrayReference arURLs = (ArrayReference) vURLs;
      List<Value> values = arURLs.getValues();
     
      //XXX: get all StringReferences first, then find() in new thread (so method returns and eventSet may be resumed)
      for(Value vURL: values) {
        ObjectReference or = (ObjectReference) vURL;
        //logClazz.debug("   u:"+vURL.toString()+"/"+vURL.type());
       
        //Value v3 = simpleInvokeVMMethod(thr, or, "toString");
        StringReference vURLstr = (StringReference) simpleInvokeVMMethod(thr, or, "toString");
        String svURLstr = vURLstr.value();
        logClazz.debug("   url: "+svURLstr);
       
        //classPathJarsTracked.add(sr.value());
        String pathFoundTmp = find(svURLstr, rt.name());
        if(pathFound==null) {
          pathFound = pathFoundTmp;
        }
        //XXX: option to not test for shadowed classes (for performance improvement)
        else {
          if(pathFoundTmp!=null && !pathFound.equals(pathFoundTmp)) {
            classesShadowed.add(pathFoundTmp);
          }
        }
      }
     
      if(pathFound!=null) {
        //classesLoaded.add(pathFound);
        classesLoadedClassLoaders.put(pathFound, iHashCode);
        String jar = pathFound.substring(0, pathFound.indexOf(ZIP_FILE_SEPARATOR));
        jarsLoaded.add(jar);
        return;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
 
  void classloaderGetParentDebug(ThreadReference thr, ClassLoaderReference clr, String classLoaderClassName, String iHashCode) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException {
    try {
      if(classLoaderNames.get(iHashCode)!=null) { return; }
     
      StringReference vToString = (StringReference) simpleInvokeVMMethod(thr, clr, "toString");
      String clrToString = vToString.value();
     
      classLoaderNames.put(iHashCode, clrToString);
     
      Value vParent = simpleInvokeVMMethod(thr, clr, "getParent");
      ObjectReference oParent = (ObjectReference) vParent;
     
      if(oParent!=null) {
        StringReference vParentToString = (StringReference) simpleInvokeVMMethod(thr, oParent, "toString");
        String parentToString = vParentToString.value();
        IntegerValue vParentHashCode = (IntegerValue) simpleInvokeVMMethod(thr, oParent, "hashCode");
        String iParentHashCode = Integer.toHexString(vParentHashCode.value());
        parentClassLoaders.put(iHashCode, iParentHashCode);
        classLoaderNames.put(iParentHashCode, parentToString);
      }
    }
    catch(NoSuchMethodException nsme) {
      //classloadersWithNoGetParentMethod.add(classLoaderClassName);
      log.warn(nsme.getMessage());
    }
  }
 
  void threadEventDebug(ThreadReference thr, String message) {
    try {
      logThread.debug(message+": " + thr.name());
    }
    catch(ObjectCollectedException e) {
      log.warn("objectcollected: "+e);
    }
    catch(RuntimeException e) {
      log.warn("exception: "+e);
    }
  }
 
  void exceptionEventDebug(ExceptionEvent exc) {
    try {
      ReferenceType rt = exc.exception().referenceType();
      Field field = rt.fieldByName("detailMessage");
      //detailMessage
      Value v = exc.exception().getValue(field);
      if(exc.catchLocation()!=null) {
        logException.debug("CaughtEx: [" + exc.location() + " | catchLoc: " + exc.catchLocation() + "] "+exc.exception().referenceType().name()+": "+v.toString());
      }
      else {
        logException.debug("UncaughtEx: [" + exc.location() + "] "+exc.exception().referenceType().name()+": "+v.toString());
      }
    }
    catch(RuntimeException e) {
      log.warn("ExceptionEvent: runtime-exception: "+e);
    }
  }
 
  //XXX: cache of Method by RT+methodNameString 
  Value simpleInvokeVMMethod(ThreadReference thr, ObjectReference or, String method) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, NoSuchMethodException {
    ReferenceType ort = or.referenceType();
      List<Method> methods = ort.methodsByName(method);
      if(methods.size()<=0) {
        throw new NoSuchMethodException("No '"+method+"' method on remote class '"+ort.name()+"'");
        //logClazz.warn("No '"+method+"' method on remote class '"+ort.name()+"'");
        //return null;
      }
      Method remoteMethod = methods.get(0);
    return or.invokeMethod(thr, remoteMethod, new ArrayList<Value>(), 0);
    }
   
  public static final String ZIP_FILE_SEPARATOR = "!/";
   
  String find(String surl, String className) throws ZipException, IOException {
    URL url = new URL(surl);
    String file = url.getFile();
    file = URLDecoder.decode(file, "UTF-8");
    //file = file.replaceAll("%20", " ");
    File baseFile = new File(file);
    String baseFileCanonicalPath = baseFile.getCanonicalPath();
    if(urlsNotFound.contains(baseFileCanonicalPath)) { return null; }
    if(!baseFile.exists()) {
      logClazz.warn("find(): not dir nor file...: "+baseFile);
      urlsNotFound.add(baseFile.getCanonicalPath());
      return null;
    }
    //file = file.substring(1); //XXX removes initial "/" - windows only?
    //logClazz.debug("   file: "+file);
    int i = className.lastIndexOf('.');
    String simpleClassName = className.substring(i+1);
    String pathName = className.substring(0,i);
    pathName = pathName.replace('.', '/');
    String fullClassName = pathName+"/"+simpleClassName+".class";
    File fileToSearch = new File(file+'/'+pathName);
    File jarFileToSearch = baseFile;
    logClazz.trace("   find(): simpleClassname: "+simpleClassName+"; path: "+pathName+"; file: "+file+"; fullClassName: "+fullClassName);
   
    String pathFound = "";
   
    if(fileToSearch.isDirectory()) {
      String[] files = fileToSearch.list();
      for(String s: files) {
        String filename = fileToSearch.getAbsolutePath()+File.separator+s;
        if(s.contains(simpleClassName)) {
          pathFound = fileToSearch.getAbsolutePath();
          logClazz.debug("   find(): found on: "+filename);
          return pathFound;
        }
      }
      return null;
    }
    else if(jarFileToSearch.exists()) {
      jarsInExistence.add(jarFileToSearch.getCanonicalPath());
      JarFile jar = new JarFile(jarFileToSearch);
     
      pathFound = findInJar(jar, fullClassName);
      if(pathFound!=null) { return pathFound; }
     
      //search in jars pointed by manifest.mf
      File parent = jarFileToSearch.getParentFile();
      Manifest manifest = jar.getManifest();
      if(manifest!=null) {
        logClazz.debug("   find(): searching in manifest's class-path jars..."); //:: "+manifest);
          Attributes atts = manifest.getMainAttributes(); //"Class-Path");

          String cp = atts.getValue(Attributes.Name.CLASS_PATH);
          String[] cps = cp.split("[ ]+");
          //StringBuffer sb = new StringBuffer();
        //sb.append("   jim: ");
          for(String s: cps) {
            //cada um desses eh um jar...
            String jarInManifestPath = parent.getCanonicalPath()+'/'+s;
            File jarInManifestPathFile = new File(jarInManifestPath);
            jarInManifestPath = jarInManifestPathFile.getCanonicalPath();
           
            if(urlsNotFound.contains(jarInManifestPath)) { continue; }
            try {
              JarFile jarFromManifest = new JarFile(jarInManifestPath); //aqui ocorre o FNFE
              //sb.append(s+"/"+jarFromManifest.getName()+" ; ");
              jarsInExistence.add(jarInManifestPath);

              String pathFoundTmp = findInJar(jarFromManifest, fullClassName);
              //if path already found do not search in other jars
              if(pathFound==null) {
                pathFound = pathFoundTmp;
              }
            }
            catch(FileNotFoundException fnfe) {
              logClazz.trace("   find(): FNFE: "+fnfe);
              urlsNotFound.add(jarInManifestPath);
              //fnfe.printStackTrace();
            }
          }
        if(pathFound!=null) {
          //logClazz.debug(sb.toString());
          return pathFound;
        }
         
        //logClazz.debug(sb.toString());
          //out.println("   atts-CP:: "+atts.getValue(Attributes.Name.CLASS_PATH));
      }
      return null;
    }
    else {
      //not dir nor jar...
      logClazz.warn("find()-arrrg!: not dir nor file...: "+fileToSearch);
      //logClazz.warn("not dir nor file...: "+fileToSearch+"/"+jarFileToSearch);
      urlsNotFound.add(fileToSearch.getCanonicalPath());
      return null; //pathFound;
    }
  }
 
  String findInJar(JarFile jar, String sClassName) throws IOException {
    String jarFileToSearchAbsPath = new File(jar.getName()).getCanonicalPath();
    Set<String> jarContents = jarContentsCache.get(jarFileToSearchAbsPath);
    if(jarContents==null) {
      jarContents = new HashSet<String>();
      Enumeration<JarEntry> entries = (Enumeration<JarEntry>) jar.entries();
      while(entries.hasMoreElements()) {
        JarEntry ze = entries.nextElement();
        jarContents.add(ze.getName());
      }
      jarContentsCache.put(jarFileToSearchAbsPath, jarContents);
    }
    for(String entry: jarContents) {
      if(entry.contains(sClassName)) {
        String outfilename = jarFileToSearchAbsPath+ZIP_FILE_SEPARATOR+entry;
        logClazz.debug("   findInJar(): found on: "+outfilename+"; className: "+sClassName+"; entry: "+entry);
        return outfilename;
      }
    }
    /*String outfilename = jarFileToSearchAbsPath+ZIP_FILE_SEPARATOR+ze.getName();
   
    if(outfilename.contains(sClassName)) {
      //pathFound = jarFileToSearch.getCanonicalPath();
      logClazz.debug("   found on: "+outfilename);
      return outfilename;
      //return pathFound;
    }*/
   
    return null;
  }

  String oldfindInJar(JarFile jar, String sClassName) {
    Enumeration<JarEntry> entries = (Enumeration<JarEntry>) jar.entries();
    String jarFileToSearchAbsPath = jar.getName();

    while(entries.hasMoreElements()) {
      JarEntry ze = entries.nextElement();

      String outfilename = jarFileToSearchAbsPath+ZIP_FILE_SEPARATOR+ze.getName();
      //String outfilename = jarFileToSearch.getAbsolutePath()+ZIP_FILE_SEPARATOR+ze.getName();

      if(outfilename.contains(sClassName)) {
        //pathFound = jarFileToSearch.getCanonicalPath();
        logClazz.debug("   oldFindInJar(): found on: "+outfilename);
        return outfilename;
        //return pathFound;
      }
    }
    return null;
  }

  void showJarsUsed() {
    long iniTime = System.currentTimeMillis();
    logAnalysis.info("classes used ["+classesLoadedClassLoaders.size()+"]:");
    for(String s: classesLoadedClassLoaders.keySet()) {
      logAnalysis.info("  "+s+" [cl:"+classesLoadedClassLoaders.get(s)+"]");
    }
    /*for(String s: classesLoaded) {
      logAnalysis.info("  "+s);
    }*/
   
    logAnalysis.info("classes shadowed ["+classesShadowed.size()+"]:");
    for(String s: classesShadowed) {
      logAnalysis.info("  "+s);
    }

    logAnalysis.info("classLoaders with no 'getURLs' method ["+classloadersWithNoGetURLsMethod.size()+"]:");
    for(String s: classloadersWithNoGetURLsMethod) {
      logAnalysis.info("  "+s);
    }

    logAnalysis.info("jars in existence ["+jarsInExistence.size()+"]:");
    for(String s: jarsInExistence) {
      logAnalysis.info("  "+s);
    }
   
    logAnalysis.info("jars not found ["+urlsNotFound.size()+"]:");
    for(String s: urlsNotFound) {
      logAnalysis.info("  "+s);
    }
   
    logAnalysis.info("jars used ["+jarsLoaded.size()+"]:");
    for(String s: jarsLoaded) {
      logAnalysis.info("  "+s);
    }
   
    Set<String> jarsNotUsed = new HashSet<String>();
    jarsNotUsed.addAll(jarsInExistence);
    jarsNotUsed.removeAll(jarsLoaded);
    logAnalysis.info("jars not used ["+jarsNotUsed.size()+"]:");
    for(String s: jarsNotUsed) {
      logAnalysis.info("  "+s);
    }
   
    logAnalysis.info("classloaders ["+classLoaderNames.size()+"]:");
    for(String key: classLoaderNames.keySet()) {
      logAnalysis.info("  "+key+": "+classLoaderNames.get(key)+" [parent: "+parentClassLoaders.get(key)+"]");
    }
   
    logAnalysis.info("-- runned at "+new Date()+", took "+runningTime+"ms [analisys time: "+(System.currentTimeMillis()-iniTime)+"ms]");
  }

}
TOP

Related Classes of tbrugz.rtcoverage.JDIEventProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.