Commit | Line | Data |
---|---|---|
ef7f180d AM |
1 | /******************************************************************************* |
2 | * Copyright (c) 2015 EfficiOS Inc., Alexandre Montplaisir | |
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 | ||
10 | package org.eclipse.tracecompass.internal.lttng2.ust.core.analysis.debuginfo; | |
11 | ||
12 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; | |
13 | ||
14 | import java.util.HashMap; | |
15 | import java.util.Map; | |
16 | ||
17 | import org.eclipse.jdt.annotation.NonNull; | |
18 | import org.eclipse.tracecompass.internal.lttng2.ust.core.Activator; | |
19 | import org.eclipse.tracecompass.internal.lttng2.ust.core.trace.layout.LttngUst28EventLayout; | |
20 | import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace; | |
21 | import org.eclipse.tracecompass.lttng2.ust.core.trace.layout.ILttngUstEventLayout; | |
22 | import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; | |
23 | import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; | |
24 | import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; | |
25 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; | |
26 | import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; | |
27 | import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider; | |
28 | import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; | |
29 | ||
30 | import com.google.common.collect.ImmutableMap; | |
31 | import com.google.common.io.BaseEncoding; | |
32 | ||
33 | /** | |
34 | * State provider for the debuginfo analysis. It tracks the layout of shared | |
35 | * libraries loaded in memory by the application. | |
36 | * | |
37 | * The layout of the generated attribute tree will look like this: | |
38 | * | |
39 | * <pre> | |
40 | * * [root] | |
41 | * +-- 1000 | |
42 | * +-- 2000 | |
43 | * ... | |
44 | * +-- 3000 (VPIDs) | |
45 | * +-- baddr (value = addr range end, long) | |
46 | * | +-- buildId (value = /path/to/library (sopath), string) | |
47 | * +-- baddr | |
48 | * | +-- buildId1 | |
49 | * | +-- buildId2 (if the same address is re-used later) | |
50 | * ... | |
51 | * </pre> | |
52 | * | |
53 | * The "baddr" attribute name will represent the range start as a string, and | |
54 | * its value will be range end. If null, it means this particular library is not | |
55 | * loaded at this location at the moment. | |
56 | * | |
57 | * This sits under the mtime (modification time of the file) attribute. This is | |
58 | * to handle cases like multiple concurrent dlopen's of the same library, or the | |
59 | * very mind-blowing edge case of a file being modified and being reloaded later | |
60 | * on, possibly side-by-side with its previous version. | |
61 | * | |
62 | * Since the state system is not a spatial database, it's not really worth | |
63 | * indexing by memory ranges, and since the amount of loaded libraries is | |
64 | * usually small, we should afford to iterate through all mappings to find each | |
65 | * match. | |
66 | * | |
67 | * Still, for better scalability (and for science), it could be interesting to | |
68 | * look into storing the memory-model-over-time in something like an R-Tree. | |
69 | * | |
70 | * @author Alexandre Montplaisir | |
71 | */ | |
72 | public class UstDebugInfoStateProvider extends AbstractTmfStateProvider { | |
73 | ||
74 | /* Version of this state provider */ | |
75 | private static final int VERSION = 1; | |
76 | ||
77 | private static final int DL_DLOPEN_INDEX = 1; | |
78 | private static final int DL_BUILD_ID_INDEX = 2; | |
79 | private static final int DL_DEBUG_LINK_INDEX = 3; | |
80 | private static final int DL_DLCLOSE_INDEX = 4; | |
81 | private static final int STATEDUMP_SOINFO_INDEX = 5; | |
82 | private static final int STATEDUMP_BUILD_ID_INDEX = 6; | |
83 | private static final int STATEDUMP_DEBUG_LINK_INDEX = 7; | |
84 | ||
85 | private final LttngUst28EventLayout fLayout; | |
86 | private final Map<String, Integer> fEventNames; | |
87 | ||
88 | /** | |
89 | * We need both the soinfo/dlopen event AND the matching build_id/debug_link | |
90 | * event to get all the information about a particular binary. | |
91 | * | |
92 | * Between these two events, we will store the <baddr, sopath> in here. | |
93 | */ | |
94 | private final Map<Long, String> fPendingEntries = new HashMap<>(); | |
95 | ||
96 | /** | |
97 | * Constructor | |
98 | * | |
99 | * @param trace | |
100 | * trace | |
101 | */ | |
102 | public UstDebugInfoStateProvider(LttngUstTrace trace) { | |
103 | super(trace, "Ust:DebugInfo"); //$NON-NLS-1$ | |
104 | ILttngUstEventLayout layout = trace.getEventLayout(); | |
105 | if (!(layout instanceof LttngUst28EventLayout)) { | |
106 | /* This analysis only support UST 2.8+ traces */ | |
107 | throw new IllegalStateException("Debug info analysis was started with an incompatible trace."); //$NON-NLS-1$ | |
108 | } | |
109 | fLayout = (LttngUst28EventLayout) layout; | |
110 | fEventNames = buildEventNames(fLayout); | |
111 | } | |
112 | ||
113 | private static Map<String, Integer> buildEventNames(LttngUst28EventLayout layout) { | |
114 | ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder(); | |
115 | builder.put(layout.eventDlOpen(), DL_DLOPEN_INDEX); | |
116 | builder.put(layout.eventDlBuildId(), DL_BUILD_ID_INDEX); | |
117 | builder.put(layout.eventDlDebugLink(), DL_DEBUG_LINK_INDEX); | |
118 | builder.put(layout.eventDlClose(), DL_DLCLOSE_INDEX); | |
119 | builder.put(layout.eventStatedumpSoInfo(), STATEDUMP_SOINFO_INDEX); | |
120 | builder.put(layout.eventStateDumpBuildId(), STATEDUMP_BUILD_ID_INDEX); | |
121 | builder.put(layout.eventStateDumpDebugLink(), STATEDUMP_DEBUG_LINK_INDEX); | |
122 | return checkNotNull(builder.build()); | |
123 | } | |
124 | ||
125 | @Override | |
126 | protected void eventHandle(ITmfEvent event) { | |
127 | /* | |
128 | * We require the "vpid" context to build the state system. The rest of | |
129 | * the analysis also needs the "ip" context, but the state provider part | |
130 | * does not. | |
131 | */ | |
132 | ITmfEventField vpidCtx = event.getContent().getField(fLayout.contextVpid()); | |
133 | if (vpidCtx == null) { | |
134 | return; | |
135 | } | |
136 | final Long vpid = (Long) vpidCtx.getValue(); | |
137 | if (vpid == null) { | |
138 | return; | |
139 | } | |
140 | ||
141 | final @NonNull ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder()); | |
142 | ||
143 | String name = event.getName(); | |
144 | Integer index = fEventNames.get(name); | |
145 | if (index == null) { | |
146 | /* Untracked event type */ | |
147 | return; | |
148 | } | |
149 | int intIndex = index.intValue(); | |
150 | ||
151 | try { | |
152 | switch (intIndex) { | |
153 | case DL_DLOPEN_INDEX: | |
154 | case STATEDUMP_SOINFO_INDEX: | |
155 | { | |
156 | handleOpen(event, vpid, ss); | |
157 | break; | |
158 | } | |
159 | ||
160 | case DL_BUILD_ID_INDEX: | |
161 | case STATEDUMP_BUILD_ID_INDEX: | |
162 | { | |
163 | handleBuildId(event, vpid, ss); | |
164 | break; | |
165 | } | |
166 | ||
167 | case DL_DEBUG_LINK_INDEX: | |
168 | case STATEDUMP_DEBUG_LINK_INDEX: | |
169 | /* Fields: Long baddr, Long crc, String filename */ | |
170 | { | |
171 | // TODO NYI | |
172 | break; | |
173 | } | |
174 | ||
175 | case DL_DLCLOSE_INDEX: | |
176 | { | |
177 | handleClose(event, vpid, ss); | |
178 | break; | |
179 | } | |
180 | ||
181 | default: | |
182 | /* Ignore other events */ | |
183 | break; | |
184 | } | |
185 | } catch (AttributeNotFoundException e) { | |
186 | Activator.getDefault().logError("Unexpected exception in UstDebugInfoStateProvider", e); //$NON-NLS-1$ | |
187 | } | |
188 | } | |
189 | ||
190 | /** | |
191 | * Handle opening a shared library. | |
192 | * | |
193 | * Uses fields: Long baddr, Long memsz, String sopath | |
194 | */ | |
195 | private void handleOpen(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) throws AttributeNotFoundException { | |
196 | Long baddr = (Long) event.getContent().getField(fLayout.fieldBaddr()).getValue(); | |
197 | Long memsz = (Long) event.getContent().getField(fLayout.fieldMemsz()).getValue(); | |
198 | String sopath = (String) event.getContent().getField(fLayout.fieldSopath()).getValue(); | |
199 | ||
200 | long endAddr = baddr.longValue() + memsz.longValue(); | |
201 | int addrQuark = ss.getQuarkAbsoluteAndAdd(vpid.toString(), baddr.toString()); | |
202 | ||
203 | long ts = event.getTimestamp().getValue(); | |
204 | ss.modifyAttribute(ts, TmfStateValue.newValueLong(endAddr), addrQuark); | |
205 | ||
206 | /* | |
207 | * Add this library to the pending entries, the matching | |
208 | * build_id/debug_link event will finish updating this attribute | |
209 | */ | |
aa353506 | 210 | fPendingEntries.put(baddr, checkNotNull(sopath)); |
ef7f180d AM |
211 | } |
212 | ||
213 | /** | |
214 | * Handle shared library build id | |
215 | * | |
216 | * Uses fields: Long baddr, long[] build_id | |
217 | */ | |
218 | private void handleBuildId(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) throws AttributeNotFoundException { | |
219 | Long baddr = (Long) event.getContent().getField(fLayout.fieldBaddr()).getValue(); | |
220 | long[] buildIdArray = checkNotNull((long[]) event.getContent().getField(fLayout.fieldBuildId()).getValue()); | |
221 | /* | |
222 | * Decode the buildID from the byte array in the trace field. | |
223 | * Use lower-case encoding, since this is how eu-readelf | |
224 | * displays it. | |
225 | */ | |
226 | String buildId = BaseEncoding.base16().encode(longArrayToByteArray(buildIdArray)).toLowerCase(); | |
227 | ||
228 | /* Retrieve the matching sopath from the pending entries */ | |
229 | String sopath = fPendingEntries.remove(baddr); | |
230 | if (sopath == null) { | |
231 | /* | |
232 | * We did not previously handle the initial event for this | |
233 | * library. Lost events? | |
234 | */ | |
235 | Activator.getDefault().logWarning("UstDebugInfoStateProvider: Received a build_id event without a matching soinfo/dlopen one."); //$NON-NLS-1$ | |
236 | return; | |
237 | } | |
238 | /* addrQuark should already exist */ | |
239 | int addrQuark = ss.getQuarkAbsolute(vpid.toString(), baddr.toString()); | |
240 | int buildIdQuark = ss.getQuarkRelativeAndAdd(addrQuark, buildId); | |
241 | long ts = event.getTimestamp().getValue(); | |
242 | ss.modifyAttribute(ts, TmfStateValue.newValueString(sopath), buildIdQuark); | |
243 | } | |
244 | ||
245 | /** | |
246 | * Handle shared library being closed | |
247 | * | |
248 | * Uses fields: Long baddr | |
249 | */ | |
250 | private void handleClose(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) { | |
251 | Long baddr = (Long) event.getContent().getField(fLayout.fieldBaddr()).getValue(); | |
252 | ||
253 | try { | |
254 | int quark = ss.getQuarkAbsolute(vpid.toString(), baddr.toString()); | |
255 | long ts = event.getTimestamp().getValue(); | |
256 | ss.removeAttribute(ts, quark); | |
257 | } catch (AttributeNotFoundException e) { | |
258 | /* | |
259 | * We have never seen a matching dlopen() for this | |
260 | * dlclose(). Possible that it happened before the start of | |
261 | * the trace, or that it was lost through lost events. | |
262 | */ | |
263 | } | |
264 | } | |
265 | ||
266 | /** | |
267 | * Until we can use Java 8 IntStream, see | |
268 | * http://stackoverflow.com/a/28008477/4227853. | |
269 | */ | |
270 | private static byte[] longArrayToByteArray(long[] array) { | |
271 | byte[] ret = new byte[array.length]; | |
272 | for (int i = 0; i < array.length; i++) { | |
273 | ret[i] = (byte) array[i]; | |
274 | } | |
275 | return ret; | |
276 | } | |
277 | ||
278 | @Override | |
279 | public ITmfStateProvider getNewInstance() { | |
280 | return new UstDebugInfoStateProvider(getTrace()); | |
281 | } | |
282 | ||
283 | @Override | |
284 | public LttngUstTrace getTrace() { | |
285 | return (LttngUstTrace) super.getTrace(); | |
286 | } | |
287 | ||
288 | @Override | |
289 | public int getVersion() { | |
290 | return VERSION; | |
291 | } | |
292 | ||
293 | } |