1 /*******************************************************************************
2 * Copyright (c) 2009 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Alvaro Sanchez-Leon (alvsan09@gmail.com) - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.lttng
.state
;
15 import java
.util
.Collections
;
16 import java
.util
.HashMap
;
17 import java
.util
.Iterator
;
18 import java
.util
.Observable
;
20 import java
.util
.Vector
;
22 import org
.eclipse
.linuxtools
.lttng
.TraceDebug
;
23 import org
.eclipse
.linuxtools
.lttng
.event
.LttngTimestamp
;
24 import org
.eclipse
.linuxtools
.lttng
.jni
.JniTrace
;
25 import org
.eclipse
.linuxtools
.lttng
.state
.evProcessor
.AbsEventProcessorFactory
;
26 import org
.eclipse
.linuxtools
.lttng
.state
.evProcessor
.EventProcessorProxy
;
27 import org
.eclipse
.linuxtools
.lttng
.state
.evProcessor
.IEventProcessing
;
28 import org
.eclipse
.linuxtools
.lttng
.state
.model
.ILttngStateInputRef
;
29 import org
.eclipse
.linuxtools
.lttng
.state
.model
.LttngTraceState
;
30 import org
.eclipse
.linuxtools
.lttng
.state
.model
.StateModelFactory
;
31 import org
.eclipse
.linuxtools
.lttng
.trace
.LTTngTrace
;
32 import org
.eclipse
.linuxtools
.tmf
.event
.TmfEvent
;
33 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimeRange
;
34 import org
.eclipse
.linuxtools
.tmf
.event
.TmfTimestamp
;
35 import org
.eclipse
.linuxtools
.tmf
.request
.TmfDataRequest
;
36 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfExperiment
;
37 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfTrace
;
38 import org
.eclipse
.linuxtools
.tmf
.trace
.TmfTraceCheckpoint
;
45 public class StateManager
extends Observable
{
47 private static final long LTTNG_STATE_SAVE_INTERVAL
= 5000000L;
49 // These are used in the building of the data request.
50 private static final long DEFAULT_OFFSET
= 0L;
51 private static final int DEFAULT_CHUNK
= 1;
53 // ========================================================================
55 // =======================================================================
56 private TmfExperiment fExperiment
= null;
57 private LTTngTrace fEventLog
= null;
58 private StateStacksHandler stateIn
= null;
59 private JniTrace trace
= null;
60 private Long eventCount
= 0L;
62 private HashMap
<Long
, LttngTraceState
> stateCheckpointsList
= new HashMap
<Long
, LttngTraceState
>();
63 private Vector
<TmfTraceCheckpoint
> timestampCheckpointsList
= new Vector
<TmfTraceCheckpoint
>();
65 // ========================================================================
67 // =======================================================================
70 // * Default constructor
72 // * Instanciate its own StateStacksHandler.
75 // public StateManager() {
76 // this.stateIn = new StateStacksHandler();
80 * Constructor with parameter
83 * @param stateInputHandler
84 * A valid StateStacksHandler
87 public StateManager(StateStacksHandler stateInputHandler
) {
88 this.stateIn
= stateInputHandler
;
95 // * @param oldStateManager
96 // * the StateManager we want to copy
99 // public StateManager(StateManager oldStateManager) {
100 // fEventLog = oldStateManager.fEventLog;
101 // stateIn = oldStateManager.stateIn;
102 // trace = oldStateManager.trace;
103 // eventCount = oldStateManager.eventCount;
105 // stateCheckpointsList = oldStateManager.stateCheckpointsList;
106 // timestampCheckpointsList = oldStateManager.timestampCheckpointsList;
109 // ========================================================================
111 // =======================================================================
113 * A new Experiment or trace selected
115 * @param clearPreviousData
117 public void setTraceSelection(TmfExperiment experiment
,
118 boolean clearPreviousData
) {
119 // New log in use, read all events and build state transition stack
120 if (experiment
!= null) {
121 if (fExperiment
!= null && fExperiment
!= experiment
) {
122 this.fExperiment
.dispose();
125 this.fExperiment
= experiment
;
127 // if (fEventLog != null) {
128 // this.fEventLog.dispose();
131 this.fEventLog
= (LTTngTrace
) experiment
.getTraces()[0];
132 trace
= fEventLog
.getCurrentJniTrace();
134 stateIn
.init(trace
, fEventLog
);
135 } catch (LttngStateException e
) {
139 // Restart count and collections
141 stateCheckpointsList
.clear();
142 timestampCheckpointsList
.clear();
144 // Obtain a dataRequest to pass to the processRequest function
145 TmfTimeRange allTraceWindow
= fEventLog
.getTimeRange();
146 StateDataRequest request
= getDataRequestStateSave(allTraceWindow
,
148 request
.setclearDataInd(clearPreviousData
);
150 // Wait for completion
151 request
.startRequestInd(fExperiment
, true, true);
153 if (TraceDebug
.isDEBUG()) {
154 StringBuilder sb
= new StringBuilder(
155 "Total number of processes in the State provider: "
156 + stateIn
.getTraceStateModel().getProcesses().length
);
158 TmfTimeRange logTimes
= fEventLog
.getTimeRange();
159 sb
.append("\n\tLog file times "
160 + new LttngTimestamp(logTimes
.getStartTime()));
161 sb
.append(" - " + new LttngTimestamp(logTimes
.getEndTime()));
163 sb
.append("\n\tCheckPoints available at: ");
164 for (TmfTraceCheckpoint cpoint
: timestampCheckpointsList
) {
165 sb
.append("\n\t" + cpoint
.getTimestamp());
167 TraceDebug
.debug(sb
.toString());
174 * TODO: Not ready for threading
176 * Read events within specific time window
181 * @param transactionID
183 public void executeDataRequest(TmfTimeRange trange
, String transactionID
,
184 IStateDataRequestListener listener
) {
185 TmfTimestamp restoredStartTime
= restoreCheckPointByTimestamp(trange
187 // Adjust the time range to consider rewinding to the start time
188 trange
= new TmfTimeRange(restoredStartTime
, trange
.getEndTime());
189 // Get a data request for the time range we want (nearest checkpoint
190 // to timestamp wanted)
193 // Process request to that point
194 StateDataRequest request
= getDataRequestByTimeRange(trange
, listener
);
195 // don't wait for completion i.e. allow cancellations
196 request
.startRequestInd(fExperiment
, false, false);
198 if (TraceDebug
.isDEBUG()) {
200 .debug(" Time Window requested, (start adjusted to checkpoint): "
201 + trange
.getStartTime()
202 + "-" + trange
.getEndTime()
203 + " Total number of processes in the State provider: "
204 + stateIn
.getTraceStateModel().getProcesses().length
+ " Completed");
209 * Current value of event counter
211 * @return Long The number of events, if it is known
213 public Long
getEventCount() {
218 * used to obtain details on the log associated with this manager e.g.
223 public TmfTrace
getEventLog() {
228 * Used for troubleshooting when debug mode is on
230 * @return Set<String> Set of event that were not handled
232 public Set
<String
> getEventsNotHandled() {
233 return stateIn
.getEventsNotHandled();
237 * Needed for verification purposes
240 * The IEventProcessing we want to register
242 void registerListener(IEventProcessing listener
) {
243 stateIn
.registerListener(listener
);
247 * Needed for verification purposes
250 * The IEventProcessing we want to unregister
252 void deregisterListener(IEventProcessing listener
) {
253 stateIn
.deregisterListener(listener
);
257 * Save a checkpoint if it is needed at that point
259 * The function will use "eventCount" internally to determine if a save was
262 * @param eventCounter
263 * The event "count" or event "id" so far
265 * The timestamp of this event
267 * @return boolean True if a checkpoint was saved, false otherwise
269 private boolean saveCheckPointIfNeeded(Long eventCounter
,
270 TmfTimestamp eventTime
) {
271 boolean saveHappened
= false;
272 // Crate new location to store checkpoint reference
273 Long location
= new Long(eventCounter
.longValue());
274 // Save a checkpoint every LTTNG_STATE_SAVE_INTERVAL event
275 if ((location
% LTTNG_STATE_SAVE_INTERVAL
) == 0) {
276 // Save the checkpoint
277 stateCheckpointsList
.put(location
, stateIn
.traceStateModel
.clone());
278 // Save correlation between timestamp and checkpoint index
280 timestampCheckpointsList
.add(new TmfTraceCheckpoint(eventTime
,
290 * Restore to the closest checkpoint from TmfTimestamp
292 * Note : it is heavier to restore by timestamp than by event position,
293 * restore by event position whichever possible.
296 * The timestamp of the event to restore to
298 * @return TmfTimestamp indicates the nearest time used to restore the
299 * state, null sent if input is invalid
301 public TmfTimestamp
restoreCheckPointByTimestamp(TmfTimestamp eventTime
) {
302 TmfTimeRange logRange
= fExperiment
.getTimeRange();
303 TmfTimestamp nearestTimeStamp
= logRange
.getStartTime();
305 // The GUI can have time limits higher than this log, since GUI can
306 // handle multiple logs
307 if ((eventTime
.getValue() < 0)
308 || (eventTime
.getValue() > logRange
.getEndTime().getValue())) {
312 // The GUI can have time limits lower than this log, since GUI can
313 // handle multiple logs
314 if ((eventTime
.getValue() < logRange
.getStartTime().getValue())) {
315 eventTime
= logRange
.getStartTime();
318 // Sort the checkpoints, required before the binary search
319 Collections
.sort(timestampCheckpointsList
);
320 // Initiate the compare with a checkpoint containing the target time
322 int index
= Collections
.binarySearch(timestampCheckpointsList
,
323 new TmfTraceCheckpoint(eventTime
, 0));
324 // adjust index to round down to earlier checkpoint when exact match not
326 index
= getPrevIndex(index
);
328 LttngTraceState traceState
;
331 // No checkpoint restore is needed, start with a brand new
333 ILttngStateInputRef inputDataRef
= new LttngStateInputRef(trace
,
335 traceState
= StateModelFactory
.getStateEntryInstance(inputDataRef
);
337 // Useful CheckPoint found
338 TmfTraceCheckpoint checkpoint
= timestampCheckpointsList
.get(index
);
339 nearestTimeStamp
= checkpoint
.getTimestamp();
340 // get the location associated with the checkpoint
341 location
= (Long
) (checkpoint
.getLocation());
342 // reference a new copy of the checkpoint template
343 traceState
= stateCheckpointsList
.get(location
).clone();
346 // Make sure eventCount stay consistent!
347 eventCount
= new Long(location
);
349 // Restore the stored traceState
350 stateIn
.setTraceStateModel(traceState
);
352 return nearestTimeStamp
;
356 * Adjust the result from a binary search to the round down position
359 * if Negative is: (-(insertion point) -1)
360 * @return position or if no match found, earlier than insertion point
362 private int getPrevIndex(int position
) {
363 int roundDownPosition
= position
;
365 roundDownPosition
= -(position
+ 2);
368 roundDownPosition
= roundDownPosition
< 0 ?
0 : roundDownPosition
;
369 return roundDownPosition
;
373 // * Restore to the closest checkpoint from position
377 // * The position of the event to restore to
379 // * @return boolean True if a checkpoint was restored, false otherwise
381 // private boolean restoreCheckPointByPosition(long position) {
382 // long nearestCheckPoint = (position - (position %
383 // LTTNG_STATE_SAVE_INTERVAL));
385 // // Some sanity check :
387 // // Not over eventCount
388 // // A checkpoint exist
389 // if ((nearestCheckPoint < 0) || (nearestCheckPoint > eventCount)
390 // || (stateCheckpointsList.get(nearestCheckPoint) == null)) {
393 // // Restore the stored traceState
394 // stateIn.setTraceStateModel(stateCheckpointsList
395 // .get(nearestCheckPoint));
397 // // Make sure eventCount stay consistent!
398 // eventCount = new Long(nearestCheckPoint);
400 // // * Rewind to the correct position
401 // // To do so, we need a request to the correct window
402 // // We will seek to nearestCheckPoint and read next events until
404 // TmfDataRequest<TmfEvent> request = getDataRequestByPosition(
405 // (int) nearestCheckPoint, (int) position);
407 // // Process request to that point
408 // fExperiment.processRequest(request, true);
415 * Get a Tmf data request for the current eventlog
418 * @param TmfTimeRange
419 * The time range we want events from.
421 * @return TmfDataRequest<TmfEvent> The request made
423 StateDataRequest
getDataRequestByTimeRange(TmfTimeRange timeWindow
,
424 IStateDataRequestListener listener
) {
426 final TmfEvent
[] evt
= new TmfEvent
[1];
429 // The override of handlePartialResult is similar to the one in
430 // getDataRequestByPosition()
433 // Create the new request and override the handlePartialResult function
434 StateDataRequest request
= new StateDataRequest(timeWindow
,
435 DEFAULT_OFFSET
, TmfDataRequest
.ALL_EVENTS
, DEFAULT_CHUNK
,
438 public void handleData() {
439 TmfEvent
[] result
= getData();
441 evt
[0] = (result
.length
> 0) ? result
[0] : null;
442 // Dispatch information for Event processing
443 stateIn
.processEvent(evt
[0]);
445 // increment internal and external number of events
446 setNumOfEvents(getNumOfEvents() + 1);
451 public void handleCompleted() {
452 if (isCancelled() || isFailed()) {
453 // No notification to end request handlers
455 // notify the associated end request handlers
467 private StateDataRequest
getDataRequestStateSave(TmfTimeRange timeWindow
,
468 IStateDataRequestListener requestListener
) {
470 final TmfEvent
[] evt
= new TmfEvent
[1];
473 // The override of handlePartialResult is similar to the one in
474 // getDataRequestByPosition()
477 // Create the new request and override the handlePartialResult function
478 StateDataRequest request
= new StateDataRequest(timeWindow
,
479 DEFAULT_OFFSET
, TmfDataRequest
.ALL_EVENTS
, DEFAULT_CHUNK
,
480 requestListener
, this) {
483 public void handleData() {
484 TmfEvent
[] result
= getData();
486 evt
[0] = (result
.length
> 0) ? result
[0] : null;
487 // Dispatch information for Event processing
488 stateIn
.processEvent(evt
[0]);
490 // Call the function that will save a checkpoint if needed at
492 // Note : We call this function before incrementing eventCount
493 // to avoid skipping the "0th" event
494 if (evt
[0] != null) {
495 saveCheckPointIfNeeded(getNumOfEvents(), evt
[0]
499 // increment internal and external counters
500 setNumOfEvents(getNumOfEvents() + 1);
505 public void handleCompleted() {
506 if (isCancelled() || isFailed()) {
507 // No notification to end request handlers
509 // notify the associated end request handlers
515 TraceDebug
.debug("number of events processed on file opening"
524 // * Get a Tmf data request for the current eventlog
527 // * @param startPosition
528 // * The position to start the get request from
529 // * @param endPosition
530 // * The position to ed the get request at
532 // * @return TmfDataRequest<TmfEvent> The request made
534 // private TmfDataRequest<TmfEvent> getDataRequestByPosition(
535 // long startPosition, long endPosition) {
536 // final TmfEvent[] evt = new TmfEvent[1];
539 // // The override of handlePartialResult is exactly the same as the one in
540 // // getDataRequestByPosition()
541 // // However, there is no way to override it in only one place to avoid
542 // // code duplication!
545 // // Create the new request and override the handlePartialResult function
546 // TmfDataRequest<TmfEvent> request = new TmfDataRequest<TmfEvent>(
547 // (int) startPosition, DEFAULT_OFFSET,
548 // (int) (endPosition - startPosition), DEFAULT_CHUNK) {
550 // public void handlePartialResult() {
551 // TmfEvent[] result = getData();
553 // evt[0] = (result.length > 0) ? result[0] : null;
554 // // Dispatch information for Event processing
555 // stateIn.processEvent(evt[0]);
565 public TmfTimeRange
getExperimentTimeWindow() {
566 if (fExperiment
!= null) {
567 return fExperiment
.getTimeRange();
573 * This method has to be called once all events of the associated Trace have
574 * been processed, this method then triggers the drawing of the final state
575 * which is necessary e.g when zooming.
577 public synchronized void requestCompleted() {
578 Set
<AbsEventProcessorFactory
> handlerRegister
= EventProcessorProxy
579 .getInstance().getProcessingFactories();
580 // Notify the FINISH handlers
581 for (Iterator
<AbsEventProcessorFactory
> iterator
= handlerRegister
582 .iterator(); iterator
.hasNext();) {
583 AbsEventProcessorFactory handlerRegistry
= (AbsEventProcessorFactory
) iterator
585 IEventProcessing handler
= handlerRegistry
.getfinishProcessor();
586 if (handler
!= null) {
587 // process State Update
588 handler
.process(null, stateIn
.traceStateModel
);