1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: RT.java,v 1.2.2.3 2004/07/16 23:32:03 vlad_r Exp $ 8 */ 9package com.vladium.emma.rt; 10 11import java.io.File; 12 13import com.vladium.logging.Logger; 14import com.vladium.util.IProperties; 15import com.vladium.util.Property; 16import com.vladium.util.exit.ExitHookManager; 17import com.vladium.emma.IAppConstants; 18import com.vladium.emma.EMMAProperties; 19import com.vladium.emma.data.ICoverageData; 20import com.vladium.emma.data.DataFactory; 21 22// ---------------------------------------------------------------------------- 23/** 24 * @author Vlad Roubtsov, (C) 2003 25 */ 26public 27abstract class RT implements IAppConstants 28{ 29 // public: ................................................................ 30 31 32 public static synchronized ICoverageData reset (final boolean createCoverageData, final boolean createExitHook) 33 { 34 // reload the app properties [needs to be done to accomodate classloader rearrangements]: 35 36 // avoid the call context tricks at runtime in case security causes problems, 37 // use an explicit caller parameter for getAppProperties(): 38 39 ClassLoader loader = RT.class.getClassLoader (); 40 if (loader == null) loader = ClassLoader.getSystemClassLoader (); 41 42 IProperties appProperties = null; 43 try 44 { 45 appProperties = EMMAProperties.getAppProperties (loader); 46 } 47 catch (Throwable t) 48 { 49 // TODO: handle better 50 t.printStackTrace (System.out); 51 } 52 s_appProperties = appProperties; 53 54 55 if (EXIT_HOOK_MANAGER != null) 56 { 57 // disable/remove the current hook, if any: 58 59 if (s_exitHook != null) 60 { 61 // note: no attempt is made to execute the existing hook, so its coverage 62 // data may be simply discarded 63 64 EXIT_HOOK_MANAGER.removeExitHook (s_exitHook); 65 s_exitHook = null; 66 } 67 } 68 69 ICoverageData cdata = s_cdata; // no sync accessor needed 70 if (createCoverageData) 71 { 72 cdata = DataFactory.newCoverageData (); 73 s_cdata = cdata; 74 } 75 else 76 { 77 s_cdata = null; 78 } 79 80 if (EXIT_HOOK_MANAGER != null) 81 { 82 if (createExitHook && (cdata != null)) 83 { 84 final Runnable exitHook = new RTExitHook (RT.class, cdata, getCoverageOutFile (), getCoverageOutMerge ()); 85 86 // FR SF978671: fault all classes that we might need to do coverage 87 // data dumping (this forces classdefs to be loaded into classloader 88 // class cache and allows output file writing to succeed even if 89 // the RT classloader is some component loader (e.g, in a J2EE container) 90 // that gets invalidated by the time the exit hook thread is run: 91 92 RTExitHook.createClassLoaderClosure (); 93 94 if (EXIT_HOOK_MANAGER.addExitHook (exitHook)) 95 { 96 s_exitHook = exitHook; 97 } 98 // else TODO: log/warn 99 } 100 } 101 102 return cdata; 103 } 104 105 public static void r (final boolean [][] coverage, final String classVMName, final long stamp) 106 { 107 // note that we use class names, not the actual Class objects, as the keys here. This 108 // is not the best possible solution because it is not capable of supporting 109 // multiply (re)loaded classes within the same app, but the rest of the toolkit 110 // isn't designed to support this anyway. Furthermore, this does not interfere 111 // with class unloading. 112 113 final ICoverageData cdata = getCoverageData (); // need to use accessor for JMM reasons 114 115 // ['cdata' can be null if a previous call to dumpCoverageData() disabled data collection] 116 117 if (cdata != null) 118 { 119 synchronized (cdata.lock ()) 120 { 121 // TODO: could something useful be communicated back to the class 122 // by returning something here [e.g., unique class ID (solves the 123 // issues of class name collisions and class reloading) or RT.class 124 // (to prevent RT reloading)] 125 126 cdata.addClass (coverage, classVMName, stamp); 127 } 128 } 129 } 130 131 public static synchronized ICoverageData getCoverageData () 132 { 133 return s_cdata; 134 } 135 136 public static synchronized IProperties getAppProperties () 137 { 138 return s_appProperties; 139 } 140 141 /** 142 * Public API for forcing coverage data dump. 143 * 144 * @param outFile 145 * @param merge 146 * @param stopDataCollection 147 */ 148 public static synchronized void dumpCoverageData (File outFile, final boolean merge, final boolean stopDataCollection) 149 { 150 if (DEBUG) System.out.println ("RT::dumpCoverageData() DUMPING " + RT.class.getClassLoader ()); 151 outFile = outFile != null ? outFile : getCoverageOutFile (); 152 153 ICoverageData cdata = s_cdata; // no need to use accessor 154 if (stopDataCollection) s_cdata = null; // TODO: log this NOTE: this does not really stop data collection, merely prevents new class registration 155 156 RTCoverageDataPersister.dumpCoverageData (cdata, ! stopDataCollection, outFile, merge); 157 } 158 159 public static synchronized void dumpCoverageData (File outFile, final boolean stopDataCollection) 160 { 161 outFile = outFile != null ? outFile : getCoverageOutFile (); 162 163 ICoverageData cdata = s_cdata; // no need to use accessor 164 if (stopDataCollection) s_cdata = null; // TODO: log this NOTE: this does not really stop data collection, merely prevents new class registration 165 166 RTCoverageDataPersister.dumpCoverageData (cdata, ! stopDataCollection, outFile, getCoverageOutMerge ()); 167 } 168 169 // protected: ............................................................. 170 171 // package: ............................................................... 172 173 // private: ............................................................... 174 175 176 private RT () {} // prevent subclassing 177 178 179 private static File getCoverageOutFile () 180 { 181 final IProperties appProperties = getAppProperties (); // sync accessor 182 if (appProperties != null) 183 { 184 final String property = appProperties.getProperty (EMMAProperties.PROPERTY_COVERAGE_DATA_OUT_FILE, 185 EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_FILE); 186 return new File (property); 187 } 188 189 return new File (EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_FILE); 190 } 191 192 private static boolean getCoverageOutMerge () 193 { 194 final IProperties appProperties = getAppProperties (); // sync accessor 195 if (appProperties != null) 196 { 197 // [Boolean.toString (boolean) is J2SDK 1.4+] 198 199 final String property = appProperties.getProperty (EMMAProperties.PROPERTY_COVERAGE_DATA_OUT_MERGE, 200 EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_MERGE.toString ()); 201 return Property.toBoolean (property); 202 } 203 204 return EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_MERGE.booleanValue (); 205 } 206 207 208 private static ICoverageData s_cdata; 209 private static Runnable s_exitHook; 210 private static IProperties s_appProperties; // TODO: this is better of as java.util.Properties 211 212 private static final ExitHookManager EXIT_HOOK_MANAGER; // set in <clinit> 213 214 private static final boolean DEBUG = false; 215 216 static 217 { 218 if (DEBUG) System.out.println ("RT[" + System.identityHashCode (RT.class) + "]::<clinit>: loaded by " + RT.class.getClassLoader ()); 219 220 ExitHookManager temp = null; 221 try 222 { 223 temp = ExitHookManager.getSingleton (); 224 } 225 catch (Throwable t) 226 { 227 // TODO: handle better 228 t.printStackTrace (System.out); 229 } 230 EXIT_HOOK_MANAGER = temp; 231 232 233 if (RTSettings.isStandaloneMode ()) 234 { 235 if (DEBUG) System.out.println ("RT::<clinit>: STANDALONE MODE"); 236 237 // load app props, create coverage data, and register an exit hook for it: 238 reset (true, true); 239 240 // use method-scoped loggers in RT: 241 final Logger log = Logger.getLogger (); 242 if (log.atINFO ()) 243 { 244 log.info ("collecting runtime coverage data ..."); 245 } 246 } 247 else 248 { 249 // load app props only: 250 reset (false, false); 251 } 252 } 253 254} // end of class 255// ----------------------------------------------------------------------------