001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 014 package org.jikesrvm.tuningfork; 015 016 import java.io.File; 017 import java.io.FileNotFoundException; 018 import java.io.FileOutputStream; 019 import java.io.IOException; 020 import java.io.OutputStream; 021 022 import org.jikesrvm.VM; 023 import org.jikesrvm.Callbacks; 024 import org.jikesrvm.Configuration; 025 import org.jikesrvm.Options; 026 import org.jikesrvm.Callbacks.ExitMonitor; 027 import org.jikesrvm.scheduler.RVMThread; 028 import org.jikesrvm.util.HashSetRVM; 029 import org.vmmagic.pragma.Uninterruptible; 030 031 import com.ibm.tuningfork.tracegen.chunk.EventChunk; 032 import com.ibm.tuningfork.tracegen.chunk.EventTypeChunk; 033 import com.ibm.tuningfork.tracegen.chunk.EventTypeSpaceChunk; 034 import com.ibm.tuningfork.tracegen.chunk.FeedHeaderChunk; 035 import com.ibm.tuningfork.tracegen.chunk.FeedletChunk; 036 import com.ibm.tuningfork.tracegen.chunk.PropertyTableChunk; 037 import com.ibm.tuningfork.tracegen.chunk.RawChunk; 038 import com.ibm.tuningfork.tracegen.types.EventAttribute; 039 import com.ibm.tuningfork.tracegen.types.EventType; 040 import com.ibm.tuningfork.tracegen.types.EventTypeSpaceVersion; 041 042 /** 043 * TuningFork Trace Engine (roughly functionally equivalent to the 044 * Logger classes in the TuningFork JavaTraceGenerationLibrary). 045 */ 046 public final class TraceEngine { 047 048 public enum State { STARTING_UP, RUNNING_FILE, SHUTTING_DOWN, SHUT_DOWN }; 049 050 public static final TraceEngine engine = new TraceEngine(); 051 private static final int IO_INTERVAL_MS = 100; 052 private static final int INITIAL_EVENT_CHUNKS = 64; 053 054 private final ChunkQueue unwrittenMetaChunks = new ChunkQueue(); 055 private final EventChunkQueue unwrittenEventChunks = new EventChunkQueue(); 056 private final EventChunkQueue availableEventChunks = new EventChunkQueue(); 057 058 private FeedletChunk activeFeedletChunk = new FeedletChunk(); 059 private EventTypeChunk activeEventTypeChunk = new EventTypeChunk(); 060 private PropertyTableChunk activePropertyTableChunk = new PropertyTableChunk(); 061 062 private int nextFeedletId = 0; 063 private final HashSetRVM<Feedlet> activeFeedlets = new HashSetRVM<Feedlet>(); 064 065 private OutputStream outputStream; 066 private State state = State.STARTING_UP; 067 068 private TraceEngine() { 069 /* Feed header and EventTypeSpaceChunk go first, so create & enqueue during bootimage writing */ 070 unwrittenMetaChunks.enqueue(new FeedHeaderChunk()); 071 unwrittenMetaChunks.enqueue(new EventTypeSpaceChunk(new EventTypeSpaceVersion("org.jikesrvm", 1))); 072 073 /* Pre-allocate all EventChunks into the bootimage so we can access them later via Untraced fields */ 074 for (int i=0; i<INITIAL_EVENT_CHUNKS; i++) { 075 availableEventChunks.enqueue(new EventChunk()); 076 } 077 } 078 079 080 public void earlyStageBooting() { 081 if (Options.TuningForkTraceFile == null) { 082 /* tracing not enabled on this run, shut down engine to minimize overhead */ 083 RVMThread.getCurrentFeedlet().enabled = false; 084 state = State.SHUT_DOWN; 085 } else { 086 unwrittenMetaChunks.enqueue(new SpaceDescriptorChunk()); 087 } 088 } 089 090 public void fullyBootedVM() { 091 if (state != State.SHUT_DOWN) { 092 String traceFile = Options.TuningForkTraceFile; 093 if (!traceFile.endsWith(".trace")) { 094 traceFile = traceFile+".trace"; 095 } 096 097 File f = new File(traceFile); 098 try { 099 outputStream = new FileOutputStream(f); 100 } catch (FileNotFoundException e) { 101 VM.sysWriteln("Unable to open trace file "+f.getAbsolutePath()); 102 VM.sysWriteln("continuing, but TuningFork trace generation is disabled."); 103 state = State.SHUT_DOWN; 104 return; 105 } 106 107 createDaemonThreads(); 108 writeInitialProperites(); 109 } 110 } 111 112 113 /** 114 * Put some basic properties about this VM build & current execution into the feed. 115 */ 116 private void writeInitialProperites() { 117 addProperty("rvm version", Configuration.RVM_VERSION_STRING); 118 addProperty("rvm config", Configuration.RVM_CONFIGURATION); 119 addProperty("Tick Frequency", "1000000000"); /* a tick is one nanosecond */ 120 } 121 122 123 /* 124 * Support for defining EventTypes 125 */ 126 127 /** 128 * Define an EventType 129 * @param name The name to give the event 130 * @param description A human readable description of the event for display in the TuningFork UI. 131 */ 132 public EventType defineEvent(String name, String description) { 133 if (state == State.SHUT_DOWN) return null; 134 EventType result = new EventType(name, description); 135 internalDefineEvent(result); 136 return result; 137 } 138 139 /** 140 * Define an EventType 141 * @param name The name to give the event 142 * @param description A human readable description of the event for display in the TuningFork UI. 143 * @param attribute Description of the event's single data value 144 */ 145 public EventType defineEvent(String name, String description, EventAttribute attribute) { 146 if (state == State.SHUT_DOWN) return null; 147 EventType result = new EventType(name, description, attribute); 148 internalDefineEvent(result); 149 return result; 150 } 151 152 /** 153 * Define an EventType 154 * @param name The name to give the event 155 * @param description A human readable description of the event for display in the TuningFork UI. 156 * @param attributes Descriptions of the event's data values 157 */ 158 public EventType defineEvent(String name, String description, EventAttribute[] attributes) { 159 if (state == State.SHUT_DOWN) return null; 160 EventType result = new EventType(name, description, attributes); 161 internalDefineEvent(result); 162 return result; 163 } 164 165 private synchronized void internalDefineEvent(EventType et) { 166 if (!activeEventTypeChunk.add(et)) { 167 activeEventTypeChunk.close(); 168 unwrittenMetaChunks.enqueue(activeEventTypeChunk); 169 activeEventTypeChunk = new EventTypeChunk(); 170 if (!activeEventTypeChunk.add(et)) { 171 if (VM.VerifyAssertions) { 172 VM.sysFail("EventTypeChunk is too small to to add event type "+et); 173 } 174 } 175 } 176 } 177 178 /* 179 * Support for Properties 180 */ 181 182 /** 183 * Add a Property (key, value) pair to the Feed. 184 * @param key the key for the property 185 * @param value the value for the property 186 */ 187 public synchronized void addProperty(String key, String value) { 188 if (state == State.SHUT_DOWN) return; 189 if (!activePropertyTableChunk.add(key, value)) { 190 activePropertyTableChunk.close(); 191 unwrittenMetaChunks.enqueue(activePropertyTableChunk); 192 activePropertyTableChunk = new PropertyTableChunk(); 193 if (!activePropertyTableChunk.add(key, value)) { 194 if (VM.VerifyAssertions) { 195 VM.sysFail("PropertyTableChunk is too small to to add "+key+" = " +value); 196 } 197 } 198 } 199 } 200 201 /* 202 * Support for Feedlets 203 */ 204 public synchronized Feedlet makeFeedlet(String name, String description) { 205 Feedlet f = new Feedlet(this, nextFeedletId++); 206 if (state == State.SHUT_DOWN) { 207 f.enabled = false; 208 return f; 209 } 210 if (!activeFeedletChunk.add(f.getFeedletIndex(), name, description)) { 211 activeFeedletChunk.close(); 212 unwrittenMetaChunks.enqueue(activeFeedletChunk); 213 activeFeedletChunk = new FeedletChunk(); 214 if (!activeFeedletChunk.add(f.getFeedletIndex(), name, description)) { 215 if (VM.VerifyAssertions) { 216 VM.sysFail("FeedletChunk is too small to to add feedlet "+name+" (" +description+")"); 217 } 218 } 219 } 220 221 activeFeedlets.add(f); 222 223 /* TODO: if we have less than 2 event chunks per active feedlet, then we should 224 * allocate more here! 225 * NOTE: We must ensure they are externally kept alive (see comment in EventChunkQueue). 226 */ 227 return f; 228 } 229 230 public synchronized void removeFeedlet(Feedlet feedlet) { 231 if (activeFeedlets.contains(feedlet)) { 232 activeFeedlets.remove(feedlet); 233 shutdownFeedlet(feedlet); 234 } 235 } 236 237 private synchronized void shutdownAllFeedlets() { 238 for (Feedlet f : activeFeedlets) { 239 shutdownFeedlet(f); 240 } 241 activeFeedlets.removeAll(); 242 } 243 244 245 private void shutdownFeedlet(Feedlet feedlet) { 246 feedlet.shutdown(); 247 if (!activeFeedletChunk.remove(feedlet.getFeedletIndex())) { 248 activeFeedletChunk.close(); 249 unwrittenMetaChunks.enqueue(activeFeedletChunk); 250 activeFeedletChunk = new FeedletChunk(); 251 if (!activeFeedletChunk.remove(feedlet.getFeedletIndex())) { 252 if (VM.VerifyAssertions) { 253 VM.sysFail("Unable to do single remove operation on a new feedlet chunk"); 254 } 255 } 256 } 257 } 258 259 /* 260 * Daemon Threads & I/O 261 */ 262 263 private void createDaemonThreads() { 264 /* Create primary I/O thread */ 265 Thread ioThread = new Thread(new Runnable() { 266 @Override 267 public void run() { 268 ioThreadMainLoop(); 269 }}, "TuningFork Primary I/O thread"); 270 ioThread.setDaemon(true); 271 ioThread.start(); 272 273 /* Install shutdown hook that will delay VM exit until I/O completes. */ 274 Callbacks.addExitMonitor(new ExitMonitor(){ 275 @Override 276 public void notifyExit(int value) { 277 state = State.SHUTTING_DOWN; 278 while (state == State.SHUTTING_DOWN) { 279 try { 280 Thread.sleep(1); 281 } catch (InterruptedException e) { 282 } 283 } 284 }}); 285 } 286 287 private void ioThreadMainLoop() { 288 state = State.RUNNING_FILE; 289 while (true) { 290 try { 291 Thread.sleep(IO_INTERVAL_MS); 292 } catch (InterruptedException e) { 293 // Do nothing. 294 } 295 boolean shouldShutDown = state == State.SHUTTING_DOWN; 296 synchronized(this) { 297 if (shouldShutDown) { 298 shutdownAllFeedlets(); 299 } 300 } 301 writeMetaChunks(); 302 writeEventChunks(); 303 if (shouldShutDown) { 304 state = State.SHUT_DOWN; 305 return; 306 } 307 } 308 } 309 310 private synchronized void writeMetaChunks() { 311 try { 312 while (!unwrittenMetaChunks.isEmpty()) { 313 RawChunk c = unwrittenMetaChunks.dequeue(); 314 c.write(outputStream); 315 } 316 if (activeEventTypeChunk != null && activeEventTypeChunk.hasData()) { 317 activeEventTypeChunk.close(); 318 activeEventTypeChunk.write(outputStream); 319 activeEventTypeChunk.reset(); 320 } 321 if (activeFeedletChunk != null && activeFeedletChunk.hasData()) { 322 activeFeedletChunk.close(); 323 activeFeedletChunk.write(outputStream); 324 activeFeedletChunk.reset(); 325 } 326 if (activePropertyTableChunk != null && activePropertyTableChunk.hasData()) { 327 activePropertyTableChunk.close(); 328 activePropertyTableChunk.write(outputStream); 329 activePropertyTableChunk.reset(); 330 } 331 } catch (IOException e) { 332 VM.sysWriteln("Exception while outputing trace TuningFork trace file"); 333 e.printStackTrace(); 334 } 335 } 336 337 private synchronized void writeEventChunks() { 338 while (!unwrittenEventChunks.isEmpty()) { 339 EventChunk c = unwrittenEventChunks.dequeue(); 340 try { 341 c.write(outputStream); 342 } catch (IOException e) { 343 VM.sysWriteln("Exception while outputing trace TuningFork trace file"); 344 e.printStackTrace(); 345 } 346 availableEventChunks.enqueue(c); /* reduce; reuse; recycle...*/ 347 } 348 } 349 350 @Uninterruptible 351 EventChunk getEventChunk() { 352 return availableEventChunks.dequeue(); 353 } 354 355 @Uninterruptible 356 public void returnFullEventChunk(EventChunk events) { 357 unwrittenEventChunks.enqueue(events); 358 } 359 360 }