Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 26f163cc901d3e6966f47fccc144c54612dd76a0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/*******************************************************************************
 * Copyright (c) 2012 Ericsson
 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 *******************************************************************************/

package org.eclipse.linuxtools.internal.tmf.core.statesystem;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
import org.eclipse.linuxtools.tmf.core.interval.TmfStateInterval;
import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
import org.eclipse.linuxtools.tmf.core.statevalue.TmfStateValue;

/**
 * The Transient State is used to build intervals from punctual state changes. It
 * contains a "state info" vector similar to the "current state", except here we
 * also record the start time of every state stored in it.
 *
 * We can then build StateInterval's, to be inserted in the State History when
 * we detect state changes : the "start time" of the interval will be the
 * recorded time we have here, and the "end time" will be the timestamp of the
 * new state-changing event we just read.
 *
 * @author alexmont
 *
 */
class TransientState {

    /* Indicates where to insert state changes that we generate */
    private final IStateHistoryBackend backend;

    private boolean isActive;
    private long latestTime;

    private ArrayList<ITmfStateValue> ongoingStateInfo;
    private final ArrayList<Long> ongoingStateStartTimes;
    private final ArrayList<Byte> stateValueTypes;

    TransientState(IStateHistoryBackend backend) {
        this.backend = backend;
        isActive = true;
        ongoingStateInfo = new ArrayList<ITmfStateValue>();
        ongoingStateStartTimes = new ArrayList<Long>();
        stateValueTypes = new ArrayList<Byte>();

        if (backend != null) {
            latestTime = backend.getStartTime();
        } else {
            latestTime = 0;
        }
    }

    long getLatestTime() {
        return latestTime;
    }

    ITmfStateValue getOngoingStateValue(int index)
            throws AttributeNotFoundException {

        checkValidAttribute(index);
        return ongoingStateInfo.get(index);
    }

    void changeOngoingStateValue(int index, ITmfStateValue newValue)
            throws AttributeNotFoundException {

        checkValidAttribute(index);
        ongoingStateInfo.set(index, newValue);
    }

    /**
     * Return the "ongoing" value for a given attribute as a dummy interval
     * whose end time = -1 (since we don't know its real end time yet).
     *
     * @param quark
     * @throws AttributeNotFoundException
     */
    ITmfStateInterval getOngoingInterval(int quark)
            throws AttributeNotFoundException {

        checkValidAttribute(quark);
        return new TmfStateInterval(ongoingStateStartTimes.get(quark), -1, quark,
                ongoingStateInfo.get(quark));
    }

    private void checkValidAttribute(int quark)
            throws AttributeNotFoundException {

        if (quark > ongoingStateInfo.size() - 1 || quark < 0) {
            throw new AttributeNotFoundException();
        }
    }

    /**
     * Batch method of changeOngoingStateValue(), updates the complete
     * ongoingStateInfo in one go. BE VERY CAREFUL WITH THIS! Especially with
     * the sizes of both arrays.
     *
     * Note that the new ongoingStateInfo will be a shallow copy of
     * newStateInfo, so that last one must be already instantiated and all.
     *
     * @param newStateInfo
     *            The List of StateValues to replace the old ongoingStateInfo
     *            one.
     */
    void changeOngoingStateInfo(ArrayList<ITmfStateValue> newStateInfo) {
        this.ongoingStateInfo = newStateInfo;
    }

    /**
     * Add an "empty line" to both "ongoing..." vectors. This is needed so the
     * Ongoing... tables can stay in sync with the number of attributes in the
     * attribute tree, namely when we add sub-path attributes.
     */
    synchronized void addEmptyEntry() {
        /*
         * Since this is a new attribute, we suppose it was in the "null state"
         * since the beginning (so we can have intervals covering for all
         * timestamps). A null interval will then get added at the first state
         * change.
         */
        ongoingStateInfo.add(TmfStateValue.nullValue());
        stateValueTypes.add((byte) -1);

        if (backend == null) {
            ongoingStateStartTimes.add(0L);
        } else {
            ongoingStateStartTimes.add(backend.getStartTime());
        }
    }

    /**
     * Ask if the state information about attribute 'quark' at time 'time' is
     * present in the Builder as it is right now. If it's not, it's either in
     * the History Tree, or not in the system at all.
     *
     * Note that this method does not return the value itself (we don't even
     * look for it, we can know by just looking at the timestamp)
     *
     * @param time
     *            The timestamp to look for
     * @param quark
     *            The quark of the attribute to look for
     * @return True if the value is present in the Transient State at this
     *         moment in time, false if it's not
     */
    boolean hasInfoAboutStateOf(long time, int quark) {
        return (this.isActive() && time >= ongoingStateStartTimes.get(quark));
    }

    /**
     * This is the lower-level method that will be called by the
     * StateHistorySystem (with already-built StateValues and timestamps)
     *
     * @param index
     *            The index in the vectors (== the quark of the attribute)
     * @param value
     *            The new StateValue associated to this attribute
     * @param eventTime
     *            The timestamp associated with this state change
     * @throws TimeRangeException
     * @throws AttributeNotFoundException
     * @throws StateValueTypeException
     */
    synchronized void processStateChange(long eventTime,
            ITmfStateValue value, int index) throws TimeRangeException,
            AttributeNotFoundException, StateValueTypeException {
        assert (this.isActive);

        byte expectedSvType = stateValueTypes.get(index);
        checkValidAttribute(index);

        /*
         * Make sure the state value type we're inserting is the same as the
         * one registered for this attribute.
         */
        if (expectedSvType == -1) {
            /*
             * The value hasn't been used yet, set it to the value
             * we're currently inserting (which might be null/-1 again).
             */
            stateValueTypes.set(index, value.getType());
        } else if ((value.getType() != -1) && (value.getType() != expectedSvType)) {
            /*
             * We authorize inserting null values in any type of attribute,
             * but for every other types, it needs to match our expectations!
             */
            throw new StateValueTypeException();
        }

        /* Update the Transient State's lastestTime, if needed */
        if (latestTime < eventTime) {
            latestTime = eventTime;
        }

        if (ongoingStateInfo.get(index).equals(value)) {
            /*
             * This is the case where the new value and the one already present
             * in the Builder are the same. We do not need to create an
             * interval, we'll just keep the current one going.
             */
            return;
        }

        if (backend != null && ongoingStateStartTimes.get(index) < eventTime) {
            /*
             * These two conditions are necessary to create an interval and
             * update ongoingStateInfo.
             */
            backend.insertPastState(ongoingStateStartTimes.get(index),
                    eventTime - 1, /* End Time */
                    index, /* attribute quark */
                    ongoingStateInfo.get(index)); /* StateValue */

            ongoingStateStartTimes.set(index, eventTime);
        }
        ongoingStateInfo.set(index, value);
        return;
    }

    /**
     * Run a "get state at time" query on the Transient State only.
     *
     * @param stateInfo
     *            The stateInfo object in which we will put our relevant
     *            information
     * @param t
     *            The requested timestamp
     */
    void doQuery(List<ITmfStateInterval> stateInfo, long t) {
        ITmfStateInterval interval;

        if (!this.isActive) {
            return;
        }
        assert (stateInfo.size() == ongoingStateInfo.size());

        for (int i = 0; i < ongoingStateInfo.size(); i++) {
            /*
             * We build a dummy interval with end time = -1 to put in the answer
             * to the query.
             */
            if (this.hasInfoAboutStateOf(t, i)) {
                interval = new TmfStateInterval(ongoingStateStartTimes.get(i), -1,
                        i, ongoingStateInfo.get(i));
                stateInfo.set(i, interval);
            }
        }
    }

    /**
     * Close off the Transient State, used for example when we are done reading a
     * static trace file. All the information currently contained in it will be
     * converted to intervals and "flushed" to the State History.
     */
    void closeTransientState(long endTime) {
        assert (this.isActive);

        for (int i = 0; i < ongoingStateInfo.size(); i++) {
            if (ongoingStateStartTimes.get(i) > endTime) {
                /*
                 * Handle the cases where trace end > timestamp of last state
                 * change. This can happen when inserting "future" changes.
                 */
                continue;
            }
            try {
                backend.insertPastState(ongoingStateStartTimes.get(i),
                        endTime, /* End Time */
                        i, /* attribute quark */
                        ongoingStateInfo.get(i)); /* StateValue */

            } catch (TimeRangeException e) {
                /*
                 * This shouldn't happen, since we control where the interval's
                 * start time comes from
                 */
                e.printStackTrace();
            }
        }

        ongoingStateInfo.clear();
        ongoingStateStartTimes.clear();
        this.isActive = false;
        return;
    }

    /**
     * Simply returns if this Transient State is currently being used or not
     *
     * @return
     */
    boolean isActive() {
        return this.isActive;
    }

    void setInactive() {
        isActive = false;
    }

    /**
     * Debugging method that prints the contents of both 'ongoing...' vectors
     *
     * @param writer
     */
    void debugPrint(PrintWriter writer) {
        /* Only used for debugging, shouldn't be externalized */
        writer.println("------------------------------"); //$NON-NLS-1$
        writer.println("Info stored in the Builder:"); //$NON-NLS-1$
        if (!this.isActive) {
            writer.println("Builder is currently inactive"); //$NON-NLS-1$
            writer.println('\n');
            return;
        }
        writer.println("\nAttribute\tStateValue\tValid since time"); //$NON-NLS-1$
        for (int i = 0; i < ongoingStateInfo.size(); i++) {
            writer.format("%d\t\t", i); //$NON-NLS-1$
            writer.print(ongoingStateInfo.get(i).toString() + "\t\t"); //$NON-NLS-1$
            writer.println(ongoingStateStartTimes.get(i).toString());
        }
        writer.println('\n');
        return;
    }

}

Back to the top