Commit | Line | Data |
---|---|---|
f9a76cac | 1 | /******************************************************************************* |
ed48dc75 | 2 | * Copyright (c) 2013, 2016 Ericsson |
f9a76cac AM |
3 | * |
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 | |
8 | * | |
9 | * Contributors: | |
10 | * Alexandre Montplaisir - Initial API and implementation | |
f606c6fa | 11 | * Matthew Khouzam - Modified to use a TreeSet |
e13bd4cd | 12 | * Patrick Tasse - Add message to exceptions |
f9a76cac AM |
13 | ******************************************************************************/ |
14 | ||
0306a843 | 15 | package org.eclipse.tracecompass.internal.statesystem.core.backend; |
f9a76cac AM |
16 | |
17 | import java.io.File; | |
18 | import java.io.FileInputStream; | |
f9a76cac | 19 | import java.util.Comparator; |
f606c6fa | 20 | import java.util.Iterator; |
f9a76cac | 21 | import java.util.List; |
866b9976 | 22 | import java.util.NavigableSet; |
f606c6fa MK |
23 | import java.util.SortedSet; |
24 | import java.util.TreeSet; | |
f9a76cac | 25 | |
b2f62cb5 | 26 | import org.eclipse.jdt.annotation.NonNull; |
0306a843 | 27 | import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend; |
e894a508 AM |
28 | import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; |
29 | import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; | |
30 | import org.eclipse.tracecompass.statesystem.core.interval.TmfStateInterval; | |
31 | import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; | |
aa353506 | 32 | import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; |
f9a76cac AM |
33 | |
34 | /** | |
35 | * State history back-end that stores its intervals in RAM only. It cannot be | |
36 | * saved to disk, which means we need to rebuild it every time we re-open a | |
37 | * trace. But it's relatively quick to build, so this shouldn't be a problem in | |
38 | * most cases. | |
39 | * | |
40 | * This should only be used with very small state histories (and/or, very small | |
41 | * traces). Since it's stored in standard Collections, it's limited to 2^31 | |
42 | * intervals. | |
43 | * | |
44 | * @author Alexandre Montplaisir | |
45 | */ | |
46 | public class InMemoryBackend implements IStateHistoryBackend { | |
47 | ||
f606c6fa MK |
48 | /** |
49 | * We need to compare the end time and the attribute, because we can have 2 | |
50 | * intervals with the same end time (for different attributes). And TreeSet | |
51 | * needs a unique "key" per element. | |
52 | */ | |
cb42195c | 53 | private static final Comparator<ITmfStateInterval> END_COMPARATOR = |
f606c6fa MK |
54 | new Comparator<ITmfStateInterval>() { |
55 | @Override | |
56 | public int compare(ITmfStateInterval o1, ITmfStateInterval o2) { | |
57 | final long e1 = o1.getEndTime(); | |
58 | final long e2 = o2.getEndTime(); | |
59 | final int a1 = o1.getAttribute(); | |
60 | final int a2 = o2.getAttribute(); | |
61 | if (e1 < e2) { | |
62 | return -1; | |
63 | } else if (e1 > e2) { | |
64 | return 1; | |
65 | } else if (a1 < a2) { | |
66 | return -1; | |
67 | } else if (a1 > a2) { | |
68 | return 1; | |
69 | } else { | |
70 | return 0; | |
71 | } | |
72 | } | |
f9a76cac | 73 | |
f606c6fa MK |
74 | }; |
75 | ||
b2f62cb5 | 76 | private final @NonNull String ssid; |
866b9976 | 77 | private final NavigableSet<ITmfStateInterval> intervals; |
f9a76cac | 78 | private final long startTime; |
b2f62cb5 | 79 | |
eaf12e04 | 80 | private volatile long latestTime; |
f9a76cac AM |
81 | |
82 | /** | |
83 | * Constructor | |
84 | * | |
b2f62cb5 AM |
85 | * @param ssid |
86 | * The state system's ID | |
f9a76cac AM |
87 | * @param startTime |
88 | * The start time of this interval store | |
89 | */ | |
b2f62cb5 AM |
90 | public InMemoryBackend(@NonNull String ssid, long startTime) { |
91 | this.ssid = ssid; | |
f9a76cac AM |
92 | this.startTime = startTime; |
93 | this.latestTime = startTime; | |
a4524c1b | 94 | this.intervals = new TreeSet<>(END_COMPARATOR); |
f9a76cac AM |
95 | } |
96 | ||
b2f62cb5 AM |
97 | @Override |
98 | public String getSSID() { | |
99 | return ssid; | |
100 | } | |
101 | ||
f9a76cac AM |
102 | @Override |
103 | public long getStartTime() { | |
104 | return startTime; | |
105 | } | |
106 | ||
107 | @Override | |
108 | public long getEndTime() { | |
109 | return latestTime; | |
110 | } | |
111 | ||
112 | @Override | |
113 | public void insertPastState(long stateStartTime, long stateEndTime, | |
114 | int quark, ITmfStateValue value) throws TimeRangeException { | |
115 | /* Make sure the passed start/end times make sense */ | |
116 | if (stateStartTime > stateEndTime || stateStartTime < startTime) { | |
e13bd4cd | 117 | throw new TimeRangeException(ssid + " Interval Start:" + stateStartTime + ", Interval End:" + stateEndTime + ", Backend Start:" + startTime); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
f9a76cac AM |
118 | } |
119 | ||
120 | ITmfStateInterval interval = new TmfStateInterval(stateStartTime, stateEndTime, quark, value); | |
121 | ||
eaf12e04 AM |
122 | /* Add the interval into the tree */ |
123 | synchronized (intervals) { | |
124 | intervals.add(interval); | |
125 | } | |
126 | ||
f9a76cac AM |
127 | /* Update the "latest seen time" */ |
128 | if (stateEndTime > latestTime) { | |
129 | latestTime = stateEndTime; | |
130 | } | |
f9a76cac AM |
131 | } |
132 | ||
f9a76cac AM |
133 | @Override |
134 | public void doQuery(List<ITmfStateInterval> currentStateInfo, long t) | |
135 | throws TimeRangeException { | |
136 | if (!checkValidTime(t)) { | |
e13bd4cd | 137 | throw new TimeRangeException(ssid + " Time:" + t + ", Start:" + startTime + ", End:" + latestTime); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
f9a76cac AM |
138 | } |
139 | ||
140 | /* | |
141 | * The intervals are sorted by end time, so we can binary search to get | |
142 | * the first possible interval, then only compare their start times. | |
143 | */ | |
eaf12e04 | 144 | synchronized (intervals) { |
866b9976 | 145 | Iterator<ITmfStateInterval> iter = searchforEndTime(intervals, t); |
eaf12e04 AM |
146 | for (int modCount = 0; iter.hasNext() && modCount < currentStateInfo.size();) { |
147 | ITmfStateInterval entry = iter.next(); | |
148 | final long entryStartTime = entry.getStartTime(); | |
149 | if (entryStartTime <= t) { | |
150 | /* Add this interval to the returned values */ | |
151 | currentStateInfo.set(entry.getAttribute(), entry); | |
152 | modCount++; | |
153 | } | |
f9a76cac AM |
154 | } |
155 | } | |
156 | } | |
157 | ||
158 | @Override | |
159 | public ITmfStateInterval doSingularQuery(long t, int attributeQuark) | |
ed48dc75 | 160 | throws TimeRangeException { |
f9a76cac | 161 | if (!checkValidTime(t)) { |
e13bd4cd | 162 | throw new TimeRangeException(ssid + " Time:" + t + ", Start:" + startTime + ", End:" + latestTime); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
f9a76cac AM |
163 | } |
164 | ||
165 | /* | |
166 | * The intervals are sorted by end time, so we can binary search to get | |
167 | * the first possible interval, then only compare their start times. | |
168 | */ | |
eaf12e04 | 169 | synchronized (intervals) { |
866b9976 | 170 | Iterator<ITmfStateInterval> iter = searchforEndTime(intervals, t); |
eaf12e04 AM |
171 | while (iter.hasNext()) { |
172 | ITmfStateInterval entry = iter.next(); | |
173 | final boolean attributeMatches = (entry.getAttribute() == attributeQuark); | |
174 | final long entryStartTime = entry.getStartTime(); | |
175 | if (attributeMatches) { | |
176 | if (entryStartTime <= t) { | |
177 | /* This is the droid we are looking for */ | |
178 | return entry; | |
179 | } | |
f9a76cac | 180 | } |
f606c6fa | 181 | } |
f9a76cac | 182 | } |
ed48dc75 | 183 | return null; |
f9a76cac AM |
184 | } |
185 | ||
0d26a9d0 | 186 | private boolean checkValidTime(long t) { |
f9a76cac AM |
187 | if (t >= startTime && t <= latestTime) { |
188 | return true; | |
189 | } | |
190 | return false; | |
191 | } | |
192 | ||
193 | @Override | |
194 | public void finishedBuilding(long endTime) throws TimeRangeException { | |
195 | /* Nothing to do */ | |
196 | } | |
197 | ||
198 | @Override | |
199 | public FileInputStream supplyAttributeTreeReader() { | |
200 | /* Saving to disk not supported */ | |
201 | return null; | |
202 | } | |
203 | ||
204 | @Override | |
205 | public File supplyAttributeTreeWriterFile() { | |
206 | /* Saving to disk not supported */ | |
207 | return null; | |
208 | } | |
209 | ||
210 | @Override | |
211 | public long supplyAttributeTreeWriterFilePosition() { | |
212 | /* Saving to disk not supported */ | |
213 | return -1; | |
214 | } | |
215 | ||
216 | @Override | |
217 | public void removeFiles() { | |
218 | /* Nothing to do */ | |
219 | } | |
220 | ||
221 | @Override | |
222 | public void dispose() { | |
223 | /* Nothing to do */ | |
224 | } | |
225 | ||
866b9976 | 226 | private static Iterator<ITmfStateInterval> searchforEndTime(NavigableSet<ITmfStateInterval> tree, long time) { |
aa353506 | 227 | ITmfStateInterval dummyInterval = new TmfStateInterval(-1, time, -1, TmfStateValue.nullValue()); |
f606c6fa MK |
228 | ITmfStateInterval myInterval = tree.lower(dummyInterval); |
229 | if (myInterval == null) { | |
230 | return tree.iterator(); | |
f9a76cac | 231 | } |
f606c6fa MK |
232 | final SortedSet<ITmfStateInterval> tailSet = tree.tailSet(myInterval); |
233 | Iterator<ITmfStateInterval> retVal = tailSet.iterator(); | |
234 | retVal.next(); | |
235 | return retVal; | |
f9a76cac AM |
236 | } |
237 | ||
238 | } |