lttng: Make use of "is_pic" event field in debug info analysis
[deliverable/tracecompass.git] / lttng / org.eclipse.tracecompass.lttng2.ust.core / src / org / eclipse / tracecompass / internal / lttng2 / ust / core / analysis / debuginfo / UstDebugInfoStateProvider.java
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 * | +- is_pic (value = 0 or 1 (int))
48 * +-- baddr
49 * | +-- buildId1
50 * | +- is_pic
51 * | +-- buildId2 (if the same address is re-used later)
52 * | +- is_pic
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
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
83 /* Version of this state provider */
84 private static final int VERSION = 2;
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;
90 private static final int STATEDUMP_BIN_INFO_INDEX = 5;
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 *
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.
108 */
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 }
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);
144 builder.put(layout.eventStatedumpBinInfo(), STATEDUMP_BIN_INFO_INDEX);
145 builder.put(layout.eventStateDumpBuildId(), STATEDUMP_BUILD_ID_INDEX);
146 builder.put(layout.eventStateDumpDebugLink(), STATEDUMP_DEBUG_LINK_INDEX);
147 return builder.build();
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:
179 case STATEDUMP_BIN_INFO_INDEX:
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 */
220 private void handleOpen(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) {
221 Long baddr = (Long) event.getContent().getField(fLayout.fieldBaddr()).getValue();
222 Long memsz = (Long) event.getContent().getField(fLayout.fieldMemsz()).getValue();
223 String sopath = (String) event.getContent().getField(fLayout.fieldPath()).getValue();
224
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
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 */
239 BinInfo binInfo = new BinInfo(baddr, checkNotNull(sopath), isPicVal.intValue());
240 fPendingEntries.put(binInfo.fBaddr, binInfo);
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 */
259 BinInfo binInfo = fPendingEntries.remove(baddr);
260 if (binInfo == null) {
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());
270
271 /* build-id attribute */
272 int buildIdQuark = ss.getQuarkRelativeAndAdd(addrQuark, buildId);
273 long ts = event.getTimestamp().getValue();
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);
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 }
This page took 0.039103 seconds and 5 git commands to generate.