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 org.jikesrvm.VM;
017    import org.jikesrvm.runtime.Time;
018    import org.vmmagic.pragma.Inline;
019    import org.vmmagic.pragma.NoInline;
020    import org.vmmagic.pragma.Uninterruptible;
021    import org.vmmagic.pragma.Untraced;
022    
023    import com.ibm.tuningfork.tracegen.chunk.EventChunk;
024    import 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
040    public 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
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    }