tmf: Add plugins for custom charts
[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 import java.util.logging.Logger;
17
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
21 import org.eclipse.tracecompass.internal.lttng2.ust.core.trace.layout.LttngUst28EventLayout;
22 import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace;
23 import org.eclipse.tracecompass.lttng2.ust.core.trace.layout.ILttngUstEventLayout;
24 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
25 import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
26 import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
27 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
28 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
29 import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider;
30 import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
31 import org.eclipse.tracecompass.tmf.core.util.Pair;
32
33 import com.google.common.collect.ImmutableMap;
34 import com.google.common.io.BaseEncoding;
35
36 /**
37 * State provider for the debuginfo analysis. It tracks the layout of shared
38 * libraries loaded in memory by the application.
39 *
40 * The layout of the generated attribute tree will look like this:
41 *
42 * <pre>
43 * Key Value
44 * /vpid null
45 * /vpid/<baddr> Integer 1 if the memory mapping active at this
46 * point in time, null otherwise.
47 * /vpid/<baddr>/build_id Build ID of the binary as an hex string, e.g.
48 * "0123456789abcdef", or null if it doesn't have a
49 * build id.
50 * /vpid/<baddr>/debug_link Path to the separate debug info of the binary, e.g.
51 * "/usr/lib/libhello.so.debug", or null if it doesn't
52 * have separate debug info.
53 * /vpid/<baddr>/memsz Size of the memory mapping in bytes.
54 * /vpid/<baddr>/path Path to the binary, e.g. "/usr/lib/libhello.so".
55 * /vpid/<baddr>/is_pic Integer 1 if the binary contains position
56 * independent code, 0 otherwise.
57 * </pre>
58 *
59 * The "baddr" attribute name represents the memory mapping base address a
60 * string (in decimal).
61 *
62 * @author Alexandre Montplaisir
63 * @author Simon Marchi
64 */
65 public class UstDebugInfoStateProvider extends AbstractTmfStateProvider {
66
67 /** State system attribute name for the in-memory binary size */
68 public static final String MEMSZ_ATTRIB = "memsz"; //$NON-NLS-1$
69
70 /** State system attribute name for the binary path */
71 public static final String PATH_ATTRIB = "path"; //$NON-NLS-1$
72
73 /** State system attribute name for the PICness of the binary */
74 public static final String IS_PIC_ATTRIB = "is_pic"; //$NON-NLS-1$
75
76 /** State system attribute name for the build ID of the binary */
77 public static final String BUILD_ID_ATTRIB = "build_id"; //$NON-NLS-1$
78
79 /** State system attribute name for the debug link of the binary */
80 public static final String DEBUG_LINK_ATTRIB = "debug_link"; //$NON-NLS-1$
81
82 /** Version of this state provider */
83 private static final int VERSION = 3;
84
85 private static final Logger LOGGER = TraceCompassLog.getLogger(UstDebugInfoStateProvider.class);
86
87 private static final int DL_DLOPEN_INDEX = 1;
88 private static final int DL_BUILD_ID_INDEX = 2;
89 private static final int DL_DEBUG_LINK_INDEX = 3;
90 private static final int DL_DLCLOSE_INDEX = 4;
91 private static final int STATEDUMP_BIN_INFO_INDEX = 5;
92 private static final int STATEDUMP_BUILD_ID_INDEX = 6;
93 private static final int STATEDUMP_DEBUG_LINK_INDEX = 7;
94 private static final int STATEDUMP_START_INDEX = 8;
95
96 private final LttngUst28EventLayout fLayout;
97 private final Map<String, Integer> fEventNames;
98
99 /*
100 * Store for data that is incomplete, for which we are waiting for some
101 * upcoming events (build_id or debug_link). Maps <vpid, baddr> to
102 * PendingBinInfo object.
103 */
104 private final Map<Pair<Long, Long>, PendingBinInfo> fPendingBinInfos = new HashMap<>();
105
106 private class PendingBinInfo {
107
108 /* The event data, saved here until we put everything in the state system. */
109 private final long fVpid;
110 private final long fBaddr;
111 private final long fMemsz;
112 private final String fPath;
113 private final boolean fIsPIC;
114
115 private @Nullable String fBuildId = null;
116 private @Nullable String fDebugLink = null;
117
118 /* Which info are we waiting for? */
119 private boolean fBuildIdPending;
120 private boolean fDebugLinkPending;
121
122 public PendingBinInfo(boolean hasBuildId, boolean hasDebugLink,
123 long vpid, long baddr, long memsz, String path, boolean isPIC) {
124 fVpid = vpid;
125 fBaddr = baddr;
126 fMemsz = memsz;
127 fPath = path;
128 fIsPIC = isPIC;
129
130 fBuildIdPending = hasBuildId;
131 fDebugLinkPending = hasDebugLink;
132 }
133
134 boolean done() {
135 return !(fBuildIdPending || fDebugLinkPending);
136 }
137
138 public void setBuildId(String buildId) {
139 fBuildIdPending = false;
140 fBuildId = buildId;
141 }
142
143 public @Nullable String getBuildId() {
144 return fBuildId;
145 }
146
147 public void setDebugLink(String debugLink) {
148 fDebugLinkPending = false;
149 fDebugLink = debugLink;
150 }
151
152 public @Nullable String getDebugLink() {
153 return fDebugLink;
154 }
155 }
156
157 /**
158 * Constructor
159 *
160 * @param trace
161 * trace
162 */
163 public UstDebugInfoStateProvider(LttngUstTrace trace) {
164 super(trace, "Ust:DebugInfo"); //$NON-NLS-1$
165 ILttngUstEventLayout layout = trace.getEventLayout();
166 if (!(layout instanceof LttngUst28EventLayout)) {
167 /* This analysis only support UST 2.8+ traces */
168 throw new IllegalStateException("Debug info analysis was started with an incompatible trace."); //$NON-NLS-1$
169 }
170 fLayout = (LttngUst28EventLayout) layout;
171 fEventNames = buildEventNames(fLayout);
172 }
173
174 private static Map<String, Integer> buildEventNames(LttngUst28EventLayout layout) {
175 ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder();
176 builder.put(layout.eventDlOpen(), DL_DLOPEN_INDEX);
177 builder.put(layout.eventDlBuildId(), DL_BUILD_ID_INDEX);
178 builder.put(layout.eventDlDebugLink(), DL_DEBUG_LINK_INDEX);
179 builder.put(layout.eventDlClose(), DL_DLCLOSE_INDEX);
180 builder.put(layout.eventStatedumpBinInfo(), STATEDUMP_BIN_INFO_INDEX);
181 builder.put(layout.eventStateDumpBuildId(), STATEDUMP_BUILD_ID_INDEX);
182 builder.put(layout.eventStateDumpDebugLink(), STATEDUMP_DEBUG_LINK_INDEX);
183 builder.put(layout.eventStatedumpStart(), STATEDUMP_START_INDEX);
184 return builder.build();
185 }
186
187 @Override
188 protected void eventHandle(ITmfEvent event) {
189 /*
190 * We require the "vpid" context to build the state system. The rest of
191 * the analysis also needs the "ip" context, but the state provider part
192 * does not.
193 */
194 final Long vpid = event.getContent().getFieldValue(Long.class, fLayout.contextVpid());
195 if (vpid == null) {
196 return;
197 }
198
199 final @NonNull ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder());
200
201 String name = event.getName();
202 Integer index = fEventNames.get(name);
203 if (index == null) {
204 /* Untracked event type */
205 return;
206 }
207 int intIndex = index.intValue();
208
209 switch (intIndex) {
210 case STATEDUMP_START_INDEX: {
211 handleStatedumpStart(event, vpid, ss);
212 break;
213 }
214
215 case DL_DLOPEN_INDEX:
216 case STATEDUMP_BIN_INFO_INDEX: {
217 handleOpen(event, vpid, ss);
218 break;
219 }
220
221 case DL_BUILD_ID_INDEX:
222 case STATEDUMP_BUILD_ID_INDEX: {
223 handleBuildId(event, vpid, ss);
224 break;
225 }
226
227 case DL_DEBUG_LINK_INDEX:
228 case STATEDUMP_DEBUG_LINK_INDEX: {
229 handleDebugLink(event, vpid, ss);
230 break;
231 }
232
233 case DL_DLCLOSE_INDEX: {
234 handleClose(event, vpid, ss);
235 break;
236 }
237
238 default:
239 /* Ignore other events */
240 break;
241 }
242 }
243
244 /**
245 * Commit the binary information contained in pending to the state system.
246 *
247 * This method should only be called when there is no more pending
248 * information for that binary.
249 */
250 private static void commitPendingToStateSystem(PendingBinInfo pending,
251 long ts, ITmfStateSystemBuilder ss) {
252 if (!pending.done()) {
253 throw new IllegalStateException();
254 }
255
256 long vpid = pending.fVpid;
257 long baddr = pending.fBaddr;
258 long memsz = pending.fMemsz;
259 String path = pending.fPath;
260 String buildId = pending.getBuildId();
261 String debugLink = pending.getDebugLink();
262 boolean isPIC = pending.fIsPIC;
263
264 /* Create the "top-level" attribute for this object. */
265 int baddrQuark = ss.getQuarkAbsoluteAndAdd(Long.toString(vpid), Long.toString(baddr));
266
267 /* Create the attributes that contain actual data. */
268 int memszQuark = ss.getQuarkRelativeAndAdd(baddrQuark, MEMSZ_ATTRIB);
269 int pathQuark = ss.getQuarkRelativeAndAdd(baddrQuark, PATH_ATTRIB);
270 int isPICQuark = ss.getQuarkRelativeAndAdd(baddrQuark, IS_PIC_ATTRIB);
271 int buildIdQuark = ss.getQuarkRelativeAndAdd(baddrQuark, BUILD_ID_ATTRIB);
272 int debugLinkQuark = ss.getQuarkRelativeAndAdd(baddrQuark, DEBUG_LINK_ATTRIB);
273 try {
274 ss.modifyAttribute(ts, TmfStateValue.newValueInt(1), baddrQuark);
275 ss.modifyAttribute(ts, TmfStateValue.newValueLong(memsz), memszQuark);
276 ss.modifyAttribute(ts, TmfStateValue.newValueString(path), pathQuark);
277 ss.modifyAttribute(ts, TmfStateValue.newValueInt(isPIC ? 1 : 0), isPICQuark);
278 if (buildId != null) {
279 ss.modifyAttribute(ts, TmfStateValue.newValueString(buildId), buildIdQuark);
280 } else {
281 ss.modifyAttribute(ts, TmfStateValue.nullValue(), buildIdQuark);
282 }
283
284 if (debugLink != null) {
285 ss.modifyAttribute(ts, TmfStateValue.newValueString(debugLink), debugLinkQuark);
286 } else {
287 ss.modifyAttribute(ts, TmfStateValue.nullValue(), debugLinkQuark);
288 }
289 } catch (StateValueTypeException e) {
290 /* Something went very wrong. */
291 throw new IllegalStateException(e);
292 }
293 }
294
295 /**
296 * Locate a PendingBinInfo object in the map of pending binary informations
297 * with the key <vpid, baddr>. Remove it from the map and return it if it is
298 * found, return null otherwise.
299 */
300 private @Nullable PendingBinInfo retrievePendingBinInfo(long vpid, long baddr) {
301 Pair<Long, Long> key = new Pair<>(vpid, baddr);
302
303 return fPendingBinInfos.remove(key);
304 }
305
306 /**
307 * Check whether we know everything there is to know about the binary
308 * described by pending, and if so, commit it to the state system.
309 * Otherwise, put it in the map of pending binary informations.
310 */
311 private void processPendingBinInfo(PendingBinInfo pending, long ts,
312 ITmfStateSystemBuilder ss) {
313 if (pending.done()) {
314 commitPendingToStateSystem(pending, ts, ss);
315 } else {
316 /* We are expecting more data for this binary, put in the pending map. */
317 Pair<Long, Long> key = new Pair<>(pending.fVpid, pending.fBaddr);
318
319 fPendingBinInfos.put(key, pending);
320 }
321 }
322
323 /**
324 * Handle the start of a statedump.
325 *
326 * When a process does an exec, a new statedump is done and all previous
327 * mappings are now invalid.
328 */
329 private static void handleStatedumpStart(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) {
330 try {
331 long ts = event.getTimestamp().getValue();
332 int vpidQuark = ss.getQuarkAbsolute(vpid.toString());
333
334 ss.removeAttribute(ts, vpidQuark);
335 } catch (AttributeNotFoundException e) {
336 /* We didn't know anything about this vpid yet, so there is nothing to remove. */
337 }
338 }
339
340 /**
341 * Handle opening a shared library.
342 *
343 * Uses fields: Long baddr, Long memsz, String path, Long is_pic
344 */
345 private void handleOpen(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) {
346 Long baddr = event.getContent().getFieldValue(Long.class, fLayout.fieldBaddr());
347 Long memsz = event.getContent().getFieldValue(Long.class, fLayout.fieldMemsz());
348 String path = event.getContent().getFieldValue(String.class, fLayout.fieldPath());
349 Long hasBuildIdValue = event.getContent().getFieldValue(Long.class, fLayout.fieldHasBuildId());
350 Long hasDebugLinkValue = event.getContent().getFieldValue(Long.class, fLayout.fieldHasDebugLink());
351 Long isPicValue = event.getContent().getFieldValue(Long.class, fLayout.fieldIsPic());
352
353 if (baddr == null ||
354 memsz == null ||
355 path == null ||
356 hasBuildIdValue == null ||
357 hasDebugLinkValue == null) {
358 LOGGER.warning(() -> "[UstDebugInfoStateProvider:InvalidDlOpenEvent] event=" + event.toString()); //$NON-NLS-1$
359 return;
360 }
361
362 /*
363 * The is_pic field is only present in the lttng_ust_statedump:bin_info
364 * event. Binaries loaded with dlopen always have PIC. Therefore, we
365 * start assuming isPIC is true, and change our mind if the field is
366 * present and says the opposite.
367 */
368 boolean isPic = true;
369 if (isPicValue != null) {
370 isPic = (isPicValue != 0);
371 }
372
373 boolean hasBuildId = hasBuildIdValue != 0;
374 boolean hasDebugLink = hasDebugLinkValue != 0;
375
376 long ts = event.getTimestamp().getValue();
377
378 PendingBinInfo p = new PendingBinInfo(hasBuildId, hasDebugLink, vpid, baddr, memsz, path, isPic);
379
380 processPendingBinInfo(p, ts, ss);
381 }
382
383 /**
384 * Handle shared library build id
385 *
386 * Uses fields: Long baddr, long[] build_id
387 */
388 private void handleBuildId(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) {
389 long[] buildIdArray = event.getContent().getFieldValue(long[].class, fLayout.fieldBuildId());
390 Long baddr = event.getContent().getFieldValue(Long.class, fLayout.fieldBaddr());
391
392 if (buildIdArray == null || baddr == null) {
393 LOGGER.warning(() -> "[UstDebugInfoStateProvider:InvalidBuildIdEvent] event=" + event.toString()); //$NON-NLS-1$
394 return;
395 }
396
397 /*
398 * Decode the buildID from the byte array in the trace field.
399 * Use lower-case encoding, since this is how eu-readelf
400 * displays it.
401 */
402 String buildId = checkNotNull(BaseEncoding.base16().encode(longArrayToByteArray(buildIdArray)).toLowerCase());
403
404 long ts = event.getTimestamp().getValue();
405
406 PendingBinInfo p = retrievePendingBinInfo(vpid, baddr);
407
408 /*
409 * We have never seen the bin_info event this event is related to,
410 * there's nothing much we can do.
411 */
412 if (p == null) {
413 return;
414 }
415
416 p.setBuildId(buildId);
417
418 processPendingBinInfo(p, ts, ss);
419 }
420
421 private void handleDebugLink(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) {
422 Long baddr = event.getContent().getFieldValue(Long.class, fLayout.fieldBaddr());
423 String debugLink = event.getContent().getFieldValue(String.class, fLayout.fieldDebugLinkFilename());
424
425 if (baddr == null || debugLink == null) {
426 LOGGER.warning(() -> "[UstDebugInfoStateProvider:InvalidDebugLinkEvent] event=" + event.toString()); //$NON-NLS-1$
427 return;
428 }
429
430 long ts = event.getTimestamp().getValue();
431
432 PendingBinInfo pendingBinInfo = retrievePendingBinInfo(vpid, baddr);
433 if (pendingBinInfo == null) {
434 return;
435 }
436
437 pendingBinInfo.setDebugLink(debugLink);
438 processPendingBinInfo(pendingBinInfo, ts, ss);
439 }
440
441 /**
442 * Handle shared library being closed
443 *
444 * Uses fields: Long baddr
445 */
446 private void handleClose(ITmfEvent event, final Long vpid, final ITmfStateSystemBuilder ss) {
447 Long baddr = event.getContent().getFieldValue(Long.class, fLayout.fieldBaddr());
448
449 if (baddr == null) {
450 LOGGER.warning(() -> "[UstDebugInfoStateProvider:InvalidDlCloseEvent] event=" + event.toString()); //$NON-NLS-1$
451 return;
452 }
453
454 try {
455 int quark = ss.getQuarkAbsolute(vpid.toString(), baddr.toString());
456 long ts = event.getTimestamp().getValue();
457 ss.removeAttribute(ts, quark);
458 } catch (AttributeNotFoundException e) {
459 /*
460 * We have never seen a matching dlopen() for this
461 * dlclose(). Possible that it happened before the start of
462 * the trace, or that it was lost through lost events.
463 */
464 }
465 }
466
467 /**
468 * Until we can use Java 8 IntStream, see
469 * http://stackoverflow.com/a/28008477/4227853.
470 */
471 private static byte[] longArrayToByteArray(long[] array) {
472 byte[] ret = new byte[array.length];
473 for (int i = 0; i < array.length; i++) {
474 ret[i] = (byte) array[i];
475 }
476 return ret;
477 }
478
479 @Override
480 public ITmfStateProvider getNewInstance() {
481 return new UstDebugInfoStateProvider(getTrace());
482 }
483
484 @Override
485 public LttngUstTrace getTrace() {
486 return (LttngUstTrace) super.getTrace();
487 }
488
489 @Override
490 public int getVersion() {
491 return VERSION;
492 }
493 }
This page took 0.051319 seconds and 5 git commands to generate.