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 org.jikesrvm.VM;
017import org.jikesrvm.runtime.Time;
018import org.vmmagic.pragma.Inline;
019import org.vmmagic.pragma.NoInline;
020import org.vmmagic.pragma.Uninterruptible;
021import org.vmmagic.pragma.Untraced;
022
023import com.ibm.tuningfork.tracegen.chunk.EventChunk;
024import com.ibm.tuningfork.tracegen.types.EventType;
025
026/**
027 * <p>A Feedlet is a entity that is a unit of trace generation
028 * for TuningFork.  In Jikes RVM, a Feedlet is typically
029 * associated with a VM_Thread.</p>
030 *
031 * <p>Note an important assumption that a Feedlet will only
032 * be used by a single thread at a time.  All operations are
033 * unsynchronized.  This invariant is usually met because only
034 * the VM_Thread to which it is attached is allowed to perform
035 * addEvent operations on the Feedlet.  If a Feedlet is attached
036 * to something other than a VM_Thread, then this invariant must be
037 * established via external synchronization.</p>
038 */
039@Uninterruptible
040public final class Feedlet {
041  private static final boolean CHECK_TYPES = VM.VerifyAssertions;
042
043  private final TraceEngine engine;
044  private final int feedletIndex;
045  private int sequenceNumber;
046  @Untraced /* NB: Assumes EventChunk is NonMoving and externally kept alive for GC */
047  private EventChunk events;
048
049  /**
050   * Enabled is true when TF engine is enabled, false otherwise.
051   * This field is intentionally not made final to
052   *   (1) allow us to disable the boot thread's feedlet during VM booting
053   *   (2) allow us to dynamically enable/disable event generation (for eventual socket hookup)
054   *   (3) allow us to turn off event generation during VM shutdown
055   */
056  boolean enabled;
057
058  /**
059   * Create a new Feedlet.
060   * This method is only meant to be called from TraceEngine.
061   * @param engine the TraceEngine instance to which this feedlet is attached.
062   * @param feedletIndex the index to use for the Feedlet
063   */
064  Feedlet(TraceEngine engine, int feedletIndex) {
065    this.engine = engine;
066    this.feedletIndex = feedletIndex;
067    this.sequenceNumber = 0;
068    this.events = null;  /* defer actually acquiring an EventChunk until the feedlet emits its first event */
069    this.enabled = true; /* If tracing is not enabled, then TraceEngine will set this field to false. */
070  }
071
072  /**
073   * @return the feedlet's index
074   */
075  int getFeedletIndex() {
076    return feedletIndex;
077  }
078
079  void shutdown() {
080    enabled = false;
081    flushEventChunk();
082  }
083
084  /**
085   * Add an event to the feedlet's generated event stream
086   * @param et The type of event to add
087   */
088  @Inline
089  public void addEvent(EventType et) {
090    if (!enabled) return;
091    addEventInternal(et);
092  }
093  @NoInline
094  private void addEventInternal(EventType et) {
095    if (CHECK_TYPES && !checkTypes(et, 0, 0, 0, 0)) return;
096
097    long timeStamp = getTimeStamp();
098    while (true) {
099      if (events == null && !acquireEventChunk()) {
100        return; /* failure */
101      }
102      if (events.addEvent(timeStamp, et)) {
103        return; /* success */
104      }
105      flushEventChunk(); /* events is full or stale; flush and try again */
106    }
107  }
108
109  /**
110   * Add an event to the feedlet's generated event stream
111   * @param et The type of event to add
112   * @param ival1 The first int data value
113   */
114  @Inline
115  public void addEvent(EventType et, int ival1) {
116    if (!enabled) return;
117    addEventInternal(et, ival1);
118  }
119  @NoInline
120  private void addEventInternal(EventType et, int ival1) {
121    if (CHECK_TYPES && !checkTypes(et, 1, 0, 0, 0)) return;
122
123    long timeStamp = getTimeStamp();
124    while (true) {
125      if (events == null && !acquireEventChunk()) {
126        return; /* failure */
127      }
128      if (events.addEvent(timeStamp, et, ival1)) {
129        return; /* success */
130      }
131      flushEventChunk(); /* events is full or stale; flush and try again */
132    }
133  }
134
135  /**
136   * Add an event to the feedlet's generated event stream
137   * @param et The type of event to add
138   * @param ival1 The first int data value
139   * @param ival2 The second int data value
140   */
141  @Inline
142  public void addEvent(EventType et, int ival1, int ival2) {
143    if (!enabled) return;
144    addEventInternal(et, ival1, ival2);
145  }
146  @NoInline
147  private void addEventInternal(EventType et, int ival1, int ival2) {
148    if (CHECK_TYPES && !checkTypes(et, 2, 0, 0, 0)) return;
149
150    long timeStamp = getTimeStamp();
151    while (true) {
152      if (events == null && !acquireEventChunk()) {
153        return; /* failure */
154      }
155      if (events.addEvent(timeStamp, et, ival1, ival2)) {
156        return; /* success */
157      }
158      flushEventChunk(); /* events is full or stale; flush and try again */
159    }
160  }
161
162  /**
163   * Add an event to the feedlet's generated event stream
164   * @param et The type of event to add
165   * @param ival1 The first int data value
166   * @param ival2 The second int data value
167   * @param ival3 The third int data value
168   */
169  @Inline
170  public void addEvent(EventType et, int ival1, int ival2, int ival3) {
171    if (!enabled) return;
172    addEventInternal(et, ival1, ival2, ival3);
173  }
174  @NoInline
175  private void addEventInternal(EventType et, int ival1, int ival2, int ival3) {
176    if (CHECK_TYPES && !checkTypes(et, 3, 0, 0, 0)) return;
177
178    long timeStamp = getTimeStamp();
179    while (true) {
180      if (events == null && !acquireEventChunk()) {
181        return; /* failure */
182      }
183      if (events.addEvent(timeStamp, et, ival1, ival2, ival3)) {
184        return; /* success */
185      }
186      flushEventChunk(); /* events is full or stale; flush and try again */
187    }
188  }
189
190  /**
191   * Add an event to the feedlet's generated event stream
192   * @param et The type of event to add
193   * @param ival1 The first int data value
194   * @param ival2 The second int data value
195   * @param ival3 The third int data value
196   * @param ival4 The fourth int data value
197   */
198  @Inline
199  public void addEvent(EventType et, int ival1, int ival2, int ival3, int ival4) {
200    if (!enabled) return;
201    addEventInternal(et, ival1, ival2, ival3, ival4);
202  }
203  @NoInline
204  private void addEventInternal(EventType et, int ival1, int ival2, int ival3, int ival4) {
205    if (CHECK_TYPES && !checkTypes(et, 4, 0, 0, 0)) return;
206
207    long timeStamp = getTimeStamp();
208    while (true) {
209      if (events == null && !acquireEventChunk()) {
210        return; /* failure */
211      }
212      if (events.addEvent(timeStamp, et, ival1, ival2, ival3, ival4)) {
213        return; /* success */
214      }
215      flushEventChunk(); /* events is full or stale; flush and try again */
216    }
217  }
218
219  /**
220   * Add an event to the feedlet's generated event stream
221   * @param et The type of event to add
222   * @param lval1 The first double data value
223   */
224  @Inline
225  public void addEvent(EventType et, long lval1) {
226    if (!enabled) return;
227    addEventInternal(et, lval1);
228  }
229  @NoInline
230  private void addEventInternal(EventType et, long lval1) {
231    if (CHECK_TYPES && !checkTypes(et, 0, 1, 0, 0)) return;
232
233    long timeStamp = getTimeStamp();
234    while (true) {
235      if (events == null && !acquireEventChunk()) {
236        return; /* failure */
237      }
238      if (events.addEvent(timeStamp, et, lval1)) {
239        return; /* success */
240      }
241      flushEventChunk(); /* events is full or stale; flush and try again */
242    }
243  }
244
245  /**
246   * Add an event to the feedlet's generated event stream
247   * @param et The type of event to add
248   * @param dval1 The first double data value
249   */
250  @Inline
251  public void addEvent(EventType et, double dval1) {
252    if (!enabled) return;
253    addEventInternal(et, dval1);
254  }
255  @NoInline
256  private void addEventInternal(EventType et, double dval1) {
257    if (CHECK_TYPES && !checkTypes(et, 0, 0, 1, 0)) return;
258
259    long timeStamp = getTimeStamp();
260    while (true) {
261      if (events == null && !acquireEventChunk()) {
262        return; /* failure */
263      }
264      if (events.addEvent(timeStamp, et, dval1)) {
265        return; /* success */
266      }
267      flushEventChunk(); /* events is full or stale; flush and try again */
268    }
269  }
270
271  /**
272   * Add an event to the feedlet's generated event stream
273   * @param et The type of event to add
274   * @param sval1 The first String data value
275   */
276  @Inline
277  public void addEvent(EventType et, String sval1) {
278    if (!enabled) return;
279    addEventInternal(et, sval1);
280  }
281  @NoInline
282  private void addEventInternal(EventType et, String sval1) {
283    if (CHECK_TYPES && !checkTypes(et, 0, 0, 0, 1)) return;
284
285    long timeStamp = getTimeStamp();
286    while (true) {
287      if (events == null && !acquireEventChunk()) {
288        return; /* failure */
289      }
290      if (events.addEvent(timeStamp, et, sval1)) {
291        return; /* success */
292      }
293      flushEventChunk(); /* events is full or stale; flush and try again */
294    }
295  }
296
297  /**
298   * Add an event to the feedlet's generated event stream
299   * @param et The type of event to add
300   * @param ival1 The first int data value
301   * @param dval1 The first double data value
302   */
303  @Inline
304  public void addEvent(EventType et, int ival1, double dval1) {
305    if (!enabled) return;
306    addEventInternal(et, ival1, dval1);
307  }
308  @NoInline
309  private void addEventInternal(EventType et, int ival1, double dval1) {
310    if (CHECK_TYPES && !checkTypes(et, 1, 0, 1, 0)) return;
311
312    long timeStamp = getTimeStamp();
313    while (true) {
314      if (events == null && !acquireEventChunk()) {
315        return; /* failure */
316      }
317      if (events.addEvent(timeStamp, et, ival1, dval1)) {
318        return; /* success */
319      }
320      flushEventChunk(); /* events is full or stale; flush and try again */
321    }
322  }
323
324  /**
325   * Add an event to the feedlet's generated event stream
326   * @param et The type of event to add
327   * @param ival1 The first int data value
328   * @param ival2 The second int data value
329   * @param dval1 The first double data value
330   */
331  @Inline
332  public void addEvent(EventType et, int ival1, int ival2, double dval1) {
333    if (!enabled) return;
334    addEventInternal(et, ival1, ival2, dval1);
335  }
336  @NoInline
337  private void addEventInternal(EventType et, int ival1, int ival2, double dval1) {
338    if (CHECK_TYPES && !checkTypes(et, 2, 0, 1, 0)) return;
339
340    long timeStamp = getTimeStamp();
341    while (true) {
342      if (events == null && !acquireEventChunk()) {
343        return; /* failure */
344      }
345      if (events.addEvent(timeStamp, et, ival1, ival2, dval1)) {
346        return; /* success */
347      }
348      flushEventChunk(); /* events is full or stale; flush and try again */
349    }
350  }
351
352  /**
353   * Add an event to the feedlet's generated event stream
354   * @param et The type of event to add
355   * @param dval1 The first double data value
356   * @param sval1 The first String data value
357   */
358  @Inline
359  public void addEvent(EventType et, double dval1, String sval1) {
360    if (!enabled) return;
361    addEventInternal(et, dval1, sval1);
362  }
363  @NoInline
364  private void addEventInternal(EventType et, double dval1, String sval1) {
365    if (CHECK_TYPES && !checkTypes(et, 0, 0, 1, 1)) return;
366
367    long timeStamp = getTimeStamp();
368    while (true) {
369      if (events == null && !acquireEventChunk()) {
370        return; /* failure */
371      }
372      if (events.addEvent(timeStamp, et, dval1, sval1)) {
373        return; /* success */
374      }
375      flushEventChunk(); /* events is full or stale; flush and try again */
376    }
377  }
378
379  /**
380   * Add an event to the feedlet's generated event stream
381   * @param et the event's type
382   * @param idata an array of int data values (may be null if no such values for this event)
383   * @param ldata an array of long data values (may be null if no such values for this event)
384   * @param ddata an array of double data values (may be null if no such values for this event)
385   * @param sdata an array of String data values (may be null if no such values for this event)
386   */
387  @Inline
388  public void addEvent(EventType et, int[] idata, long[] ldata, double[] ddata, String[] sdata) {
389    if (!enabled) return;
390    addEventInternal(et, idata, ldata, ddata, sdata);
391  }
392  @NoInline
393  private void addEventInternal(EventType et, int[] idata, long[] ldata, double[] ddata, String[] sdata) {
394    if (CHECK_TYPES) {
395      int ilen = idata == null ? 0 : idata.length;
396      int llen = ldata == null ? 0 : ldata.length;
397      int dlen = ddata == null ? 0 : ddata.length;
398      int slen = sdata == null ? 0 : sdata.length;
399      if (!checkTypes(et, ilen, llen, dlen, slen)) return;
400    }
401
402    long timeStamp = getTimeStamp();
403    while (true) {
404      if (events == null && !acquireEventChunk()) {
405        return; /* failure */
406      }
407      if (events.addEvent(timeStamp, et, idata, ldata, ddata, sdata)) {
408        return; /* success */
409      }
410      flushEventChunk(); /* events is full or stale; flush and try again */
411    }
412  }
413
414  private boolean checkTypes(EventType et, int numInts, int numLongs, int numDoubles, int numStrings) {
415    if (et.getNumberOfInts() != numInts ||
416        et.getNumberOfLongs() != numLongs ||
417        et.getNumberOfDoubles() != numDoubles ||
418        et.getNumberOfStrings() != numStrings) {
419      VM.sysWriteln("Feedlet: Dropping addEvent of ill-typed event ", et.getName());
420      return false;
421    } else {
422      return true;
423    }
424  }
425
426  private long getTimeStamp() {
427    return Time.nanoTime();
428  }
429
430  @NoInline
431  private boolean acquireEventChunk() {
432    if (VM.VerifyAssertions) VM._assert(events == null);
433    events = engine.getEventChunk();
434    if (events == null) {
435      // TODO: here is where we would need to record in the Feedlet's
436      //       state that we had to drop an event.  We would then later
437      //       (when we got an event chunk back again) emit an event
438      //       to indicate the number of events that were dropped due to
439      //       an inability to obtain an event chunk.
440      return false;
441    }
442    events.reset(feedletIndex, sequenceNumber++);
443    return true;
444  }
445
446  @NoInline
447  private void flushEventChunk() {
448    if (events != null) {
449      events.close();
450      engine.returnFullEventChunk(events);
451      events = null;
452    }
453  }
454}