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) | |
d89151ba | 47 | * | +- is_pic (value = 0 or 1 (int)) |
ef7f180d AM |
48 | * +-- baddr |
49 | * | +-- buildId1 | |
d89151ba | 50 | * | +- is_pic |
ef7f180d | 51 | * | +-- buildId2 (if the same address is re-used later) |
d89151ba | 52 | * | +- is_pic |
ef7f180d AM |
53 | * ... |
54 | * </pre> | |
55 | * | |
56 | * The "baddr" attribute name will represent the range start as a string, and | |
57 | * its value will be range end. If null, it means this particular library is not | |
58 | * loaded at this location at the moment. | |
59 | * | |
60 | * This sits under the mtime (modification time of the file) attribute. This is | |
61 | * to handle cases like multiple concurrent dlopen's of the same library, or the | |
62 | * very mind-blowing edge case of a file being modified and being reloaded later | |
63 | * on, possibly side-by-side with its previous version. | |
64 | * | |
65 | * Since the state system is not a spatial database, it's not really worth | |
66 | * indexing by memory ranges, and since the amount of loaded libraries is | |
67 | * usually small, we should afford to iterate through all mappings to find each | |
68 | * match. | |
69 | * | |
70 | * Still, for better scalability (and for science), it could be interesting to | |
71 | * look into storing the memory-model-over-time in something like an R-Tree. | |
72 | * | |
73 | * @author Alexandre Montplaisir | |
74 | */ | |
75 | public class UstDebugInfoStateProvider extends AbstractTmfStateProvider { | |
76 | ||
d89151ba AM |
77 | /** |
78 | * Sub-attribute indicating if a given binary is PIC (position-independent | |
79 | * code) or not. This information is present directly in the trace. | |
80 | */ | |
81 | public static final String IS_PIC_ATTRIB = "is_pic"; //$NON-NLS-1$ | |
82 | ||
ef7f180d | 83 | /* Version of this state provider */ |
d89151ba | 84 | private static final int VERSION = 2; |
ef7f180d AM |
85 | |
86 | private static final int DL_DLOPEN_INDEX = 1; | |
87 | private static final int DL_BUILD_ID_INDEX = 2; | |
88 | private static final int DL_DEBUG_LINK_INDEX = 3; | |
89 | private static final int DL_DLCLOSE_INDEX = 4; | |
0b49411f | 90 | private static final int STATEDUMP_BIN_INFO_INDEX = 5; |
ef7f180d AM |
91 | private static final int STATEDUMP_BUILD_ID_INDEX = 6; |
92 | private static final int STATEDUMP_DEBUG_LINK_INDEX = 7; | |
93 | ||
94 | private final LttngUst28EventLayout fLayout; | |
95 | private final Map<String, Integer> fEventNames; | |
96 | ||
97 | /** | |
98 | * We need both the soinfo/dlopen event AND the matching build_id/debug_link | |
99 | * event to get all the information about a particular binary. | |
100 | * | |
d89151ba AM |
101 | * Between these two events, we will store the <baddr, BinInfo> in here. |
102 | */ | |
103 | private final Map<Long, BinInfo> fPendingEntries = new HashMap<>(); | |
104 | ||
105 | /** | |
106 | * Information contained in a "bin_info" event, which means a binary's path, | |
107 | * base address, and if it is a PIC or not. | |
ef7f180d | 108 | */ |
d89151ba AM |
109 | private static class BinInfo { |
110 | public final long fBaddr; | |
111 | public final String fPath; | |
112 | public final int fIsPic; | |
113 | ||
114 | public BinInfo(long baddr, String path, int isPic) { | |
115 | fBaddr = baddr; | |
116 | fPath = path; | |
117 | fIsPic = isPic; | |
118 | } | |
119 | } | |
ef7f180d AM |
120 | |
121 | /** | |
122 | * Constructor | |
123 | * | |
124 | * @param trace | |
125 | * trace | |
126 | */ | |
127 | public UstDebugInfoStateProvider(LttngUstTrace trace) { | |
128 | super(trace, "Ust:DebugInfo"); //$NON-NLS-1$ | |
129 | ILttngUstEventLayout layout = trace.getEventLayout(); | |
130 | if (!(layout instanceof LttngUst28EventLayout)) { | |
131 | /* This analysis only support UST 2.8+ traces */ | |
132 | throw new IllegalStateException("Debug info analysis was started with an incompatible trace."); //$NON-NLS-1$ | |
133 | } | |
134 | fLayout = (LttngUst28EventLayout) layout; | |
135 | fEventNames = buildEventNames(fLayout); | |
136 | } | |
137 | ||
138 | private static Map<String, Integer> buildEventNames(LttngUst28EventLayout layout) { | |
139 | ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder(); | |
140 | builder.put(layout.eventDlOpen(), DL_DLOPEN_INDEX); | |
141 | builder.put(layout.eventDlBuildId(), DL_BUILD_ID_INDEX); | |
142 | builder.put(layout.eventDlDebugLink(), DL_DEBUG_LINK_INDEX); | |
143 | builder.put(layout.eventDlClose(), DL_DLCLOSE_INDEX); | |
0b49411f | 144 | builder.put(layout.eventStatedumpBinInfo(), STATEDUMP_BIN_INFO_INDEX); |
ef7f180d AM |
145 | builder.put(layout.eventStateDumpBuildId(), STATEDUMP_BUILD_ID_INDEX); |
146 | builder.put(layout.eventStateDumpDebugLink(), STATEDUMP_DEBUG_LINK_INDEX); | |
0e4f957e | 147 | return builder.build(); |
ef7f180d AM |
148 | } |
149 | ||
150 | @Override | |
151 | protected void eventHandle(ITmfEvent event) { | |
152 | /* | |
153 | * We require the "vpid" context to build the state system. The rest of | |
154 | * the analysis also needs the "ip" context, but the state provider part | |
155 | * does not. | |
156 | */ | |
157 | ITmfEventField vpidCtx = event.getContent().getField(fLayout.contextVpid()); | |
158 | if (vpidCtx == null) { | |
159 | return; | |
160 | } | |
161 | final Long vpid = (Long) vpidCtx.getValue(); | |
162 | if (vpid == null) { | |
163 | return; | |
164 | } | |
165 | ||
166 | final @NonNull ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder()); | |
167 | ||
168 | String name = event.getName(); | |
169 | Integer index = fEventNames.get(name); | |
170 | if (index == null) { | |
171 | /* Untracked event type */ | |
172 | return; | |
173 | } | |
174 | int intIndex = index.intValue(); | |
175 | ||
176 | try { | |
177 | switch (intIndex) { | |
178 | case DL_DLOPEN_INDEX: | |
0b49411f | 179 | case STATEDUMP_BIN_INFO_INDEX: |
ef7f180d AM |
180 | { |
181 | handleOpen(event, vpid, ss); | |
182 | break; | |
183 | } | |
184 | ||
185 | case DL_BUILD_ID_INDEX: | |
186 | case STATEDUMP_BUILD_ID_INDEX: | |
187 | { | |
188 | handleBuildId(event, vpid, ss); | |
189 | break; | |
190 | } | |
191 | ||
192 | case DL_DEBUG_LINK_INDEX: | |
193 | case STATEDUMP_DEBUG_LINK_INDEX: | |
194 | /* Fields: Long baddr, Long crc, String filename */ | |
195 | { | |
196 | // TODO NYI | |
197 | break; | |
198 | } | |
199 | ||
200 | case DL_DLCLOSE_INDEX: | |
201 | { | |
202 | handleClose(event, vpid, ss); | |
203 | break; | |
204 | } | |
205 | ||
206 | default: | |
207 | /* Ignore other events */ | |
208 | break; | |
209 | } | |
210 | } catch (AttributeNotFoundException e) { | |
211 | Activator.getDefault().logError("Unexpected exception in UstDebugInfoStateProvider", e); //$NON-NLS-1$ | |
212 | } | |
213 | } | |
214 | ||
215 | /** | |
216 | * Handle opening a shared library. | |
217 | * | |
218 | * Uses fields: Long baddr, Long memsz, String sopath | |
219 | */ | |
d89151ba | 220 | private void handleOpen(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) { |
ef7f180d AM |
221 | Long baddr = (Long) event.getContent().getField(fLayout.fieldBaddr()).getValue(); |
222 | Long memsz = (Long) event.getContent().getField(fLayout.fieldMemsz()).getValue(); | |
0b49411f | 223 | String sopath = (String) event.getContent().getField(fLayout.fieldPath()).getValue(); |
ef7f180d | 224 | |
d89151ba AM |
225 | /* "dlopen" events do not have a "is_pic" field, they always refer to PIC libs */ |
226 | ITmfEventField isPicField = event.getContent().getField(fLayout.fieldIsPic()); | |
227 | Long isPicVal = (isPicField == null ? 1L : (Long) isPicField.getValue()); | |
228 | ||
ef7f180d AM |
229 | long endAddr = baddr.longValue() + memsz.longValue(); |
230 | int addrQuark = ss.getQuarkAbsoluteAndAdd(vpid.toString(), baddr.toString()); | |
231 | ||
232 | long ts = event.getTimestamp().getValue(); | |
233 | ss.modifyAttribute(ts, TmfStateValue.newValueLong(endAddr), addrQuark); | |
234 | ||
235 | /* | |
236 | * Add this library to the pending entries, the matching | |
237 | * build_id/debug_link event will finish updating this attribute | |
238 | */ | |
d89151ba AM |
239 | BinInfo binInfo = new BinInfo(baddr, checkNotNull(sopath), isPicVal.intValue()); |
240 | fPendingEntries.put(binInfo.fBaddr, binInfo); | |
ef7f180d AM |
241 | } |
242 | ||
243 | /** | |
244 | * Handle shared library build id | |
245 | * | |
246 | * Uses fields: Long baddr, long[] build_id | |
247 | */ | |
248 | private void handleBuildId(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) throws AttributeNotFoundException { | |
249 | Long baddr = (Long) event.getContent().getField(fLayout.fieldBaddr()).getValue(); | |
250 | long[] buildIdArray = checkNotNull((long[]) event.getContent().getField(fLayout.fieldBuildId()).getValue()); | |
251 | /* | |
252 | * Decode the buildID from the byte array in the trace field. | |
253 | * Use lower-case encoding, since this is how eu-readelf | |
254 | * displays it. | |
255 | */ | |
256 | String buildId = BaseEncoding.base16().encode(longArrayToByteArray(buildIdArray)).toLowerCase(); | |
257 | ||
258 | /* Retrieve the matching sopath from the pending entries */ | |
d89151ba AM |
259 | BinInfo binInfo = fPendingEntries.remove(baddr); |
260 | if (binInfo == null) { | |
ef7f180d AM |
261 | /* |
262 | * We did not previously handle the initial event for this | |
263 | * library. Lost events? | |
264 | */ | |
265 | Activator.getDefault().logWarning("UstDebugInfoStateProvider: Received a build_id event without a matching soinfo/dlopen one."); //$NON-NLS-1$ | |
266 | return; | |
267 | } | |
268 | /* addrQuark should already exist */ | |
269 | int addrQuark = ss.getQuarkAbsolute(vpid.toString(), baddr.toString()); | |
d89151ba AM |
270 | |
271 | /* build-id attribute */ | |
ef7f180d AM |
272 | int buildIdQuark = ss.getQuarkRelativeAndAdd(addrQuark, buildId); |
273 | long ts = event.getTimestamp().getValue(); | |
d89151ba AM |
274 | ss.modifyAttribute(ts, TmfStateValue.newValueString(binInfo.fPath), buildIdQuark); |
275 | ||
276 | /* "is_pic" sub-attribute */ | |
277 | int isPicQuark = ss.getQuarkRelativeAndAdd(buildIdQuark, IS_PIC_ATTRIB); | |
278 | ss.modifyAttribute(ts, TmfStateValue.newValueInt(binInfo.fIsPic), isPicQuark); | |
ef7f180d AM |
279 | } |
280 | ||
281 | /** | |
282 | * Handle shared library being closed | |
283 | * | |
284 | * Uses fields: Long baddr | |
285 | */ | |
286 | private void handleClose(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) { | |
287 | Long baddr = (Long) event.getContent().getField(fLayout.fieldBaddr()).getValue(); | |
288 | ||
289 | try { | |
290 | int quark = ss.getQuarkAbsolute(vpid.toString(), baddr.toString()); | |
291 | long ts = event.getTimestamp().getValue(); | |
292 | ss.removeAttribute(ts, quark); | |
293 | } catch (AttributeNotFoundException e) { | |
294 | /* | |
295 | * We have never seen a matching dlopen() for this | |
296 | * dlclose(). Possible that it happened before the start of | |
297 | * the trace, or that it was lost through lost events. | |
298 | */ | |
299 | } | |
300 | } | |
301 | ||
302 | /** | |
303 | * Until we can use Java 8 IntStream, see | |
304 | * http://stackoverflow.com/a/28008477/4227853. | |
305 | */ | |
306 | private static byte[] longArrayToByteArray(long[] array) { | |
307 | byte[] ret = new byte[array.length]; | |
308 | for (int i = 0; i < array.length; i++) { | |
309 | ret[i] = (byte) array[i]; | |
310 | } | |
311 | return ret; | |
312 | } | |
313 | ||
314 | @Override | |
315 | public ITmfStateProvider getNewInstance() { | |
316 | return new UstDebugInfoStateProvider(getTrace()); | |
317 | } | |
318 | ||
319 | @Override | |
320 | public LttngUstTrace getTrace() { | |
321 | return (LttngUstTrace) super.getTrace(); | |
322 | } | |
323 | ||
324 | @Override | |
325 | public int getVersion() { | |
326 | return VERSION; | |
327 | } | |
328 | ||
329 | } |