/*
* This file is part of the Jikes RVM project (http://jikesrvm.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
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.tuningfork;
import org.jikesrvm.VM;
import org.jikesrvm.runtime.Time;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.pragma.Untraced;
import com.ibm.tuningfork.tracegen.chunk.EventChunk;
import com.ibm.tuningfork.tracegen.types.EventType;
/**
* <p>A Feedlet is a entity that is a unit of trace generation
* for TuningFork. In Jikes RVM, a Feedlet is typically
* associated with a VM_Thread.</p>
*
* <p>Note an important assumption that a Feedlet will only
* be used by a single thread at a time. All operations are
* unsynchronized. This invariant is usually met because only
* the VM_Thread to which it is attached is allowed to perform
* addEvent operations on the Feedlet. If a Feedlet is attached
* to something other than a VM_Thread, then this invariant must be
* established via external synchronization.</p>
*/
@Uninterruptible
public final class Feedlet {
private static final boolean CHECK_TYPES = VM.VerifyAssertions;
private final TraceEngine engine;
private final int feedletIndex;
private int sequenceNumber;
@Untraced /* NB: Assumes EventChunk is NonMoving and externally kept alive for GC */
private EventChunk events;
/**
* Enabled is true when TF engine is enabled, false otherwise.
* This field is intentionally not made final to
* (1) allow us to disable the boot thread's feedlet during VM booting
* (2) allow us to dynamically enable/disable event generation (for eventual socket hookup)
* (3) allow us to turn off event generation during VM shutdown
*/
boolean enabled;
/**
* Create a new Feedlet.
* This method is only meant to be called from TraceEngine.
* @param engine the TraceEngine instance to which this feedlet is attached.
* @param feedletIndex the index to use for the Feedlet
*/
Feedlet(TraceEngine engine, int feedletIndex) {
this.engine = engine;
this.feedletIndex = feedletIndex;
this.sequenceNumber = 0;
this.events = null; /* defer actually acquiring an EventChunk until the feedlet emits its first event */
this.enabled = true; /* If tracing is not enabled, then TraceEngine will set this field to false. */
}
/**
* @return the feedlet's index
*/
int getFeedletIndex() {
return feedletIndex;
}
void shutdown() {
enabled = false;
flushEventChunk();
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
*/
@Inline
public void addEvent(EventType et) {
if (!enabled) return;
addEventInternal(et);
}
@NoInline
private void addEventInternal(EventType et) {
if (CHECK_TYPES && !checkTypes(et, 0, 0, 0, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param ival1 The first int data value
*/
@Inline
public void addEvent(EventType et, int ival1) {
if (!enabled) return;
addEventInternal(et, ival1);
}
@NoInline
private void addEventInternal(EventType et, int ival1) {
if (CHECK_TYPES && !checkTypes(et, 1, 0, 0, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, ival1)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param ival1 The first int data value
* @param ival2 The second int data value
*/
@Inline
public void addEvent(EventType et, int ival1, int ival2) {
if (!enabled) return;
addEventInternal(et, ival1, ival2);
}
@NoInline
private void addEventInternal(EventType et, int ival1, int ival2) {
if (CHECK_TYPES && !checkTypes(et, 2, 0, 0, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, ival1, ival2)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param ival1 The first int data value
* @param ival2 The second int data value
* @param ival3 The third int data value
*/
@Inline
public void addEvent(EventType et, int ival1, int ival2, int ival3) {
if (!enabled) return;
addEventInternal(et, ival1, ival2, ival3);
}
@NoInline
private void addEventInternal(EventType et, int ival1, int ival2, int ival3) {
if (CHECK_TYPES && !checkTypes(et, 3, 0, 0, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, ival1, ival2, ival3)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param ival1 The first int data value
* @param ival2 The second int data value
* @param ival3 The third int data value
* @param ival4 The fourth int data value
*/
@Inline
public void addEvent(EventType et, int ival1, int ival2, int ival3, int ival4) {
if (!enabled) return;
addEventInternal(et, ival1, ival2, ival3, ival4);
}
@NoInline
private void addEventInternal(EventType et, int ival1, int ival2, int ival3, int ival4) {
if (CHECK_TYPES && !checkTypes(et, 4, 0, 0, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, ival1, ival2, ival3, ival4)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param lval1 The first double data value
*/
@Inline
public void addEvent(EventType et, long lval1) {
if (!enabled) return;
addEventInternal(et, lval1);
}
@NoInline
private void addEventInternal(EventType et, long lval1) {
if (CHECK_TYPES && !checkTypes(et, 0, 1, 0, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, lval1)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param dval1 The first double data value
*/
@Inline
public void addEvent(EventType et, double dval1) {
if (!enabled) return;
addEventInternal(et, dval1);
}
@NoInline
private void addEventInternal(EventType et, double dval1) {
if (CHECK_TYPES && !checkTypes(et, 0, 0, 1, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, dval1)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param sval1 The first String data value
*/
@Inline
public void addEvent(EventType et, String sval1) {
if (!enabled) return;
addEventInternal(et, sval1);
}
@NoInline
private void addEventInternal(EventType et, String sval1) {
if (CHECK_TYPES && !checkTypes(et, 0, 0, 0, 1)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, sval1)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param ival1 The first int data value
* @param dval1 The first double data value
*/
@Inline
public void addEvent(EventType et, int ival1, double dval1) {
if (!enabled) return;
addEventInternal(et, ival1, dval1);
}
@NoInline
private void addEventInternal(EventType et, int ival1, double dval1) {
if (CHECK_TYPES && !checkTypes(et, 1, 0, 1, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, ival1, dval1)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param ival1 The first int data value
* @param ival2 The second int data value
* @param dval1 The first double data value
*/
@Inline
public void addEvent(EventType et, int ival1, int ival2, double dval1) {
if (!enabled) return;
addEventInternal(et, ival1, ival2, dval1);
}
@NoInline
private void addEventInternal(EventType et, int ival1, int ival2, double dval1) {
if (CHECK_TYPES && !checkTypes(et, 2, 0, 1, 0)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, ival1, ival2, dval1)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et The type of event to add
* @param dval1 The first double data value
* @param sval1 The first String data value
*/
@Inline
public void addEvent(EventType et, double dval1, String sval1) {
if (!enabled) return;
addEventInternal(et, dval1, sval1);
}
@NoInline
private void addEventInternal(EventType et, double dval1, String sval1) {
if (CHECK_TYPES && !checkTypes(et, 0, 0, 1, 1)) return;
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, dval1, sval1)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
/**
* Add an event to the feedlet's generated event stream
* @param et the event's type
* @param idata an array of int data values (may be null if no such values for this event)
* @param ldata an array of long data values (may be null if no such values for this event)
* @param ddata an array of double data values (may be null if no such values for this event)
* @param sdata an array of String data values (may be null if no such values for this event)
*/
@Inline
public void addEvent(EventType et, int[] idata, long[] ldata, double[] ddata, String[] sdata) {
if (!enabled) return;
addEventInternal(et, idata, ldata, ddata, sdata);
}
@NoInline
private void addEventInternal(EventType et, int[] idata, long[] ldata, double[] ddata, String[] sdata) {
if (CHECK_TYPES) {
int ilen = idata == null ? 0 : idata.length;
int llen = ldata == null ? 0 : ldata.length;
int dlen = ddata == null ? 0 : ddata.length;
int slen = sdata == null ? 0 : sdata.length;
if (!checkTypes(et, ilen, llen, dlen, slen)) return;
}
long timeStamp = getTimeStamp();
while (true) {
if (events == null && !acquireEventChunk()) {
return; /* failure */
}
if (events.addEvent(timeStamp, et, idata, ldata, ddata, sdata)) {
return; /* success */
}
flushEventChunk(); /* events is full or stale; flush and try again */
}
}
private boolean checkTypes(EventType et, int numInts, int numLongs, int numDoubles, int numStrings) {
if (et.getNumberOfInts() != numInts ||
et.getNumberOfLongs() != numLongs ||
et.getNumberOfDoubles() != numDoubles ||
et.getNumberOfStrings() != numStrings) {
VM.sysWriteln("Feedlet: Dropping addEvent of ill-typed event ", et.getName());
return false;
} else {
return true;
}
}
private long getTimeStamp() {
return Time.nanoTime();
}
@NoInline
private boolean acquireEventChunk() {
if (VM.VerifyAssertions) VM._assert(events == null);
events = engine.getEventChunk();
if (events == null) {
// TODO: here is where we would need to record in the Feedlet's
// state that we had to drop an event. We would then later
// (when we got an event chunk back again) emit an event
// to indicate the number of events that were dropped due to
// an inability to obtain an event chunk.
return false;
}
events.reset(feedletIndex, sequenceNumber++);
return true;
}
@NoInline
private void flushEventChunk() {
if (events != null) {
events.close();
engine.returnFullEventChunk(events);
events = null;
}
}
}