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 014package org.jikesrvm.tuningfork; 015 016import java.io.File; 017import java.io.FileNotFoundException; 018import java.io.FileOutputStream; 019import java.io.IOException; 020import java.io.OutputStream; 021 022import org.jikesrvm.VM; 023import org.jikesrvm.runtime.Callbacks; 024import org.jikesrvm.runtime.Callbacks.ExitMonitor; 025import org.jikesrvm.Configuration; 026import org.jikesrvm.Options; 027import org.jikesrvm.scheduler.RVMThread; 028import org.jikesrvm.util.HashSetRVM; 029import org.vmmagic.pragma.Uninterruptible; 030 031import com.ibm.tuningfork.tracegen.chunk.EventChunk; 032import com.ibm.tuningfork.tracegen.chunk.EventTypeChunk; 033import com.ibm.tuningfork.tracegen.chunk.EventTypeSpaceChunk; 034import com.ibm.tuningfork.tracegen.chunk.FeedHeaderChunk; 035import com.ibm.tuningfork.tracegen.chunk.FeedletChunk; 036import com.ibm.tuningfork.tracegen.chunk.PropertyTableChunk; 037import com.ibm.tuningfork.tracegen.chunk.RawChunk; 038import com.ibm.tuningfork.tracegen.types.EventAttribute; 039import com.ibm.tuningfork.tracegen.types.EventType; 040import 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 */ 046public 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 * Defines 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 * @return the event type instance 132 */ 133 public EventType defineEvent(String name, String description) { 134 if (state == State.SHUT_DOWN) return null; 135 EventType result = new EventType(name, description); 136 internalDefineEvent(result); 137 return result; 138 } 139 140 /** 141 * Define an EventType. 142 * @param name The name to give the event 143 * @param description A human readable description of the event for display in the TuningFork UI. 144 * @param attribute Description of the event's single data value 145 * @return the event type instance 146 */ 147 public EventType defineEvent(String name, String description, EventAttribute attribute) { 148 if (state == State.SHUT_DOWN) return null; 149 EventType result = new EventType(name, description, attribute); 150 internalDefineEvent(result); 151 return result; 152 } 153 154 /** 155 * Defines an EventType. 156 * @param name The name to give the event 157 * @param description A human readable description of the event for display in the TuningFork UI. 158 * @param attributes Descriptions of the event's data values 159 * @return the event type instance 160 */ 161 public EventType defineEvent(String name, String description, EventAttribute[] attributes) { 162 if (state == State.SHUT_DOWN) return null; 163 EventType result = new EventType(name, description, attributes); 164 internalDefineEvent(result); 165 return result; 166 } 167 168 private synchronized void internalDefineEvent(EventType et) { 169 if (!activeEventTypeChunk.add(et)) { 170 activeEventTypeChunk.close(); 171 unwrittenMetaChunks.enqueue(activeEventTypeChunk); 172 activeEventTypeChunk = new EventTypeChunk(); 173 if (!activeEventTypeChunk.add(et)) { 174 if (VM.VerifyAssertions) { 175 VM.sysFail("EventTypeChunk is too small to to add event type " + et); 176 } 177 } 178 } 179 } 180 181 /* 182 * Support for Properties 183 */ 184 185 /** 186 * Add a Property (key, value) pair to the Feed. 187 * @param key the key for the property 188 * @param value the value for the property 189 */ 190 public synchronized void addProperty(String key, String value) { 191 if (state == State.SHUT_DOWN) return; 192 if (!activePropertyTableChunk.add(key, value)) { 193 activePropertyTableChunk.close(); 194 unwrittenMetaChunks.enqueue(activePropertyTableChunk); 195 activePropertyTableChunk = new PropertyTableChunk(); 196 if (!activePropertyTableChunk.add(key, value)) { 197 if (VM.VerifyAssertions) { 198 VM.sysFail("PropertyTableChunk is too small to to add " + key + " = " + value); 199 } 200 } 201 } 202 } 203 204 /* 205 * Support for Feedlets 206 */ 207 public synchronized Feedlet makeFeedlet(String name, String description) { 208 Feedlet f = new Feedlet(this, nextFeedletId++); 209 if (state == State.SHUT_DOWN) { 210 f.enabled = false; 211 return f; 212 } 213 if (!activeFeedletChunk.add(f.getFeedletIndex(), name, description)) { 214 activeFeedletChunk.close(); 215 unwrittenMetaChunks.enqueue(activeFeedletChunk); 216 activeFeedletChunk = new FeedletChunk(); 217 if (!activeFeedletChunk.add(f.getFeedletIndex(), name, description)) { 218 if (VM.VerifyAssertions) { 219 VM.sysFail("FeedletChunk is too small to to add feedlet " + name + " (" + description + ")"); 220 } 221 } 222 } 223 224 activeFeedlets.add(f); 225 226 /* TODO: if we have less than 2 event chunks per active feedlet, then we should 227 * allocate more here! 228 * NOTE: We must ensure they are externally kept alive (see comment in EventChunkQueue). 229 */ 230 return f; 231 } 232 233 public synchronized void removeFeedlet(Feedlet feedlet) { 234 if (activeFeedlets.contains(feedlet)) { 235 activeFeedlets.remove(feedlet); 236 shutdownFeedlet(feedlet); 237 } 238 } 239 240 private synchronized void shutdownAllFeedlets() { 241 for (Feedlet f : activeFeedlets) { 242 shutdownFeedlet(f); 243 } 244 activeFeedlets.removeAll(); 245 } 246 247 248 private void shutdownFeedlet(Feedlet feedlet) { 249 feedlet.shutdown(); 250 if (!activeFeedletChunk.remove(feedlet.getFeedletIndex())) { 251 activeFeedletChunk.close(); 252 unwrittenMetaChunks.enqueue(activeFeedletChunk); 253 activeFeedletChunk = new FeedletChunk(); 254 if (!activeFeedletChunk.remove(feedlet.getFeedletIndex())) { 255 if (VM.VerifyAssertions) { 256 VM.sysFail("Unable to do single remove operation on a new feedlet chunk"); 257 } 258 } 259 } 260 } 261 262 /* 263 * Daemon Threads & I/O 264 */ 265 266 private void createDaemonThreads() { 267 /* Create primary I/O thread */ 268 Thread ioThread = new Thread(new Runnable() { 269 @Override 270 public void run() { 271 ioThreadMainLoop(); 272 }}, "TuningFork Primary I/O thread"); 273 ioThread.setDaemon(true); 274 ioThread.start(); 275 276 /* Install shutdown hook that will delay VM exit until I/O completes. */ 277 Callbacks.addExitMonitor(new ExitMonitor(){ 278 @Override 279 public void notifyExit(int value) { 280 state = State.SHUTTING_DOWN; 281 while (state == State.SHUTTING_DOWN) { 282 try { 283 Thread.sleep(1); 284 } catch (InterruptedException e) { 285 } 286 } 287 }}); 288 } 289 290 private void ioThreadMainLoop() { 291 state = State.RUNNING_FILE; 292 while (true) { 293 try { 294 Thread.sleep(IO_INTERVAL_MS); 295 } catch (InterruptedException e) { 296 // Do nothing. 297 } 298 boolean shouldShutDown = state == State.SHUTTING_DOWN; 299 synchronized (this) { 300 if (shouldShutDown) { 301 shutdownAllFeedlets(); 302 } 303 } 304 writeMetaChunks(); 305 writeEventChunks(); 306 if (shouldShutDown) { 307 state = State.SHUT_DOWN; 308 return; 309 } 310 } 311 } 312 313 private synchronized void writeMetaChunks() { 314 try { 315 while (!unwrittenMetaChunks.isEmpty()) { 316 RawChunk c = unwrittenMetaChunks.dequeue(); 317 c.write(outputStream); 318 } 319 if (activeEventTypeChunk != null && activeEventTypeChunk.hasData()) { 320 activeEventTypeChunk.close(); 321 activeEventTypeChunk.write(outputStream); 322 activeEventTypeChunk.reset(); 323 } 324 if (activeFeedletChunk != null && activeFeedletChunk.hasData()) { 325 activeFeedletChunk.close(); 326 activeFeedletChunk.write(outputStream); 327 activeFeedletChunk.reset(); 328 } 329 if (activePropertyTableChunk != null && activePropertyTableChunk.hasData()) { 330 activePropertyTableChunk.close(); 331 activePropertyTableChunk.write(outputStream); 332 activePropertyTableChunk.reset(); 333 } 334 } catch (IOException e) { 335 VM.sysWriteln("Exception while outputing trace TuningFork trace file"); 336 e.printStackTrace(); 337 } 338 } 339 340 private synchronized void writeEventChunks() { 341 while (!unwrittenEventChunks.isEmpty()) { 342 EventChunk c = unwrittenEventChunks.dequeue(); 343 try { 344 c.write(outputStream); 345 } catch (IOException e) { 346 VM.sysWriteln("Exception while outputing trace TuningFork trace file"); 347 e.printStackTrace(); 348 } 349 availableEventChunks.enqueue(c); /* reduce; reuse; recycle...*/ 350 } 351 } 352 353 @Uninterruptible 354 EventChunk getEventChunk() { 355 return availableEventChunks.dequeue(); 356 } 357 358 @Uninterruptible 359 public void returnFullEventChunk(EventChunk events) { 360 unwrittenEventChunks.enqueue(events); 361 } 362 363}