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