1 /*******************************************************************************
2 * Copyright (c) 2015 EfficiOS Inc., Alexandre Montplaisir
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 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.internal
.lttng2
.ust
.core
.analysis
.debuginfo
;
12 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
14 import java
.util
.HashMap
;
16 import java
.util
.logging
.Logger
;
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
;
33 import com
.google
.common
.collect
.ImmutableMap
;
34 import com
.google
.common
.io
.BaseEncoding
;
37 * State provider for the debuginfo analysis. It tracks the layout of shared
38 * libraries loaded in memory by the application.
40 * The layout of the generated attribute tree will look like this:
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
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.
59 * The "baddr" attribute name represents the memory mapping base address a
60 * string (in decimal).
62 * @author Alexandre Montplaisir
63 * @author Simon Marchi
65 public class UstDebugInfoStateProvider
extends AbstractTmfStateProvider
{
67 /** State system attribute name for the in-memory binary size */
68 public static final String MEMSZ_ATTRIB
= "memsz"; //$NON-NLS-1$
70 /** State system attribute name for the binary path */
71 public static final String PATH_ATTRIB
= "path"; //$NON-NLS-1$
73 /** State system attribute name for the PICness of the binary */
74 public static final String IS_PIC_ATTRIB
= "is_pic"; //$NON-NLS-1$
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$
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$
82 /** Version of this state provider */
83 private static final int VERSION
= 3;
85 private static final Logger LOGGER
= TraceCompassLog
.getLogger(UstDebugInfoStateProvider
.class);
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;
96 private final LttngUst28EventLayout fLayout
;
97 private final Map
<String
, Integer
> fEventNames
;
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.
104 private final Map
<Pair
<Long
, Long
>, PendingBinInfo
> fPendingBinInfos
= new HashMap
<>();
106 private class PendingBinInfo
{
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
;
115 private @Nullable String fBuildId
= null;
116 private @Nullable String fDebugLink
= null;
118 /* Which info are we waiting for? */
119 private boolean fBuildIdPending
;
120 private boolean fDebugLinkPending
;
122 public PendingBinInfo(boolean hasBuildId
, boolean hasDebugLink
,
123 long vpid
, long baddr
, long memsz
, String path
, boolean isPIC
) {
130 fBuildIdPending
= hasBuildId
;
131 fDebugLinkPending
= hasDebugLink
;
135 return !(fBuildIdPending
|| fDebugLinkPending
);
138 public void setBuildId(String buildId
) {
139 fBuildIdPending
= false;
143 public @Nullable String
getBuildId() {
147 public void setDebugLink(String debugLink
) {
148 fDebugLinkPending
= false;
149 fDebugLink
= debugLink
;
152 public @Nullable String
getDebugLink() {
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$
170 fLayout
= (LttngUst28EventLayout
) layout
;
171 fEventNames
= buildEventNames(fLayout
);
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();
188 protected void eventHandle(ITmfEvent event
) {
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
194 final Long vpid
= event
.getContent().getFieldValue(Long
.class, fLayout
.contextVpid());
199 final @NonNull ITmfStateSystemBuilder ss
= checkNotNull(getStateSystemBuilder());
201 String name
= event
.getName();
202 Integer index
= fEventNames
.get(name
);
204 /* Untracked event type */
207 int intIndex
= index
.intValue();
210 case STATEDUMP_START_INDEX
: {
211 handleStatedumpStart(event
, vpid
, ss
);
215 case DL_DLOPEN_INDEX
:
216 case STATEDUMP_BIN_INFO_INDEX
: {
217 handleOpen(event
, vpid
, ss
);
221 case DL_BUILD_ID_INDEX
:
222 case STATEDUMP_BUILD_ID_INDEX
: {
223 handleBuildId(event
, vpid
, ss
);
227 case DL_DEBUG_LINK_INDEX
:
228 case STATEDUMP_DEBUG_LINK_INDEX
: {
229 handleDebugLink(event
, vpid
, ss
);
233 case DL_DLCLOSE_INDEX
: {
234 handleClose(event
, vpid
, ss
);
239 /* Ignore other events */
245 * Commit the binary information contained in pending to the state system.
247 * This method should only be called when there is no more pending
248 * information for that binary.
250 private static void commitPendingToStateSystem(PendingBinInfo pending
,
251 long ts
, ITmfStateSystemBuilder ss
) {
252 if (!pending
.done()) {
253 throw new IllegalStateException();
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
;
264 /* Create the "top-level" attribute for this object. */
265 int baddrQuark
= ss
.getQuarkAbsoluteAndAdd(Long
.toString(vpid
), Long
.toString(baddr
));
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
);
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
);
281 ss
.modifyAttribute(ts
, TmfStateValue
.nullValue(), buildIdQuark
);
284 if (debugLink
!= null) {
285 ss
.modifyAttribute(ts
, TmfStateValue
.newValueString(debugLink
), debugLinkQuark
);
287 ss
.modifyAttribute(ts
, TmfStateValue
.nullValue(), debugLinkQuark
);
289 } catch (StateValueTypeException e
) {
290 /* Something went very wrong. */
291 throw new IllegalStateException(e
);
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.
300 private @Nullable PendingBinInfo
retrievePendingBinInfo(long vpid
, long baddr
) {
301 Pair
<Long
, Long
> key
= new Pair
<>(vpid
, baddr
);
303 return fPendingBinInfos
.remove(key
);
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.
311 private void processPendingBinInfo(PendingBinInfo pending
, long ts
,
312 ITmfStateSystemBuilder ss
) {
313 if (pending
.done()) {
314 commitPendingToStateSystem(pending
, ts
, ss
);
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
);
319 fPendingBinInfos
.put(key
, pending
);
324 * Handle the start of a statedump.
326 * When a process does an exec, a new statedump is done and all previous
327 * mappings are now invalid.
329 private static void handleStatedumpStart(ITmfEvent event
, final Long vpid
, final ITmfStateSystemBuilder ss
) {
331 long ts
= event
.getTimestamp().getValue();
332 int vpidQuark
= ss
.getQuarkAbsolute(vpid
.toString());
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. */
341 * Handle opening a shared library.
343 * Uses fields: Long baddr, Long memsz, String path, Long is_pic
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());
356 hasBuildIdValue
== null ||
357 hasDebugLinkValue
== null) {
358 LOGGER
.warning(() -> "[UstDebugInfoStateProvider:InvalidDlOpenEvent] event=" + event
.toString()); //$NON-NLS-1$
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.
368 boolean isPic
= true;
369 if (isPicValue
!= null) {
370 isPic
= (isPicValue
!= 0);
373 boolean hasBuildId
= hasBuildIdValue
!= 0;
374 boolean hasDebugLink
= hasDebugLinkValue
!= 0;
376 long ts
= event
.getTimestamp().getValue();
378 PendingBinInfo p
= new PendingBinInfo(hasBuildId
, hasDebugLink
, vpid
, baddr
, memsz
, path
, isPic
);
380 processPendingBinInfo(p
, ts
, ss
);
384 * Handle shared library build id
386 * Uses fields: Long baddr, long[] build_id
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());
392 if (buildIdArray
== null || baddr
== null) {
393 LOGGER
.warning(() -> "[UstDebugInfoStateProvider:InvalidBuildIdEvent] event=" + event
.toString()); //$NON-NLS-1$
398 * Decode the buildID from the byte array in the trace field.
399 * Use lower-case encoding, since this is how eu-readelf
402 String buildId
= checkNotNull(BaseEncoding
.base16().encode(longArrayToByteArray(buildIdArray
)).toLowerCase());
404 long ts
= event
.getTimestamp().getValue();
406 PendingBinInfo p
= retrievePendingBinInfo(vpid
, baddr
);
409 * We have never seen the bin_info event this event is related to,
410 * there's nothing much we can do.
416 p
.setBuildId(buildId
);
418 processPendingBinInfo(p
, ts
, ss
);
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());
425 if (baddr
== null || debugLink
== null) {
426 LOGGER
.warning(() -> "[UstDebugInfoStateProvider:InvalidDebugLinkEvent] event=" + event
.toString()); //$NON-NLS-1$
430 long ts
= event
.getTimestamp().getValue();
432 PendingBinInfo pendingBinInfo
= retrievePendingBinInfo(vpid
, baddr
);
433 if (pendingBinInfo
== null) {
437 pendingBinInfo
.setDebugLink(debugLink
);
438 processPendingBinInfo(pendingBinInfo
, ts
, ss
);
442 * Handle shared library being closed
444 * Uses fields: Long baddr
446 private void handleClose(ITmfEvent event
, final Long vpid
, final ITmfStateSystemBuilder ss
) {
447 Long baddr
= event
.getContent().getFieldValue(Long
.class, fLayout
.fieldBaddr());
450 LOGGER
.warning(() -> "[UstDebugInfoStateProvider:InvalidDlCloseEvent] event=" + event
.toString()); //$NON-NLS-1$
455 int quark
= ss
.getQuarkAbsolute(vpid
.toString(), baddr
.toString());
456 long ts
= event
.getTimestamp().getValue();
457 ss
.removeAttribute(ts
, quark
);
458 } catch (AttributeNotFoundException e
) {
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.
468 * Until we can use Java 8 IntStream, see
469 * http://stackoverflow.com/a/28008477/4227853.
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
];
480 public ITmfStateProvider
getNewInstance() {
481 return new UstDebugInfoStateProvider(getTrace());
485 public LttngUstTrace
getTrace() {
486 return (LttngUstTrace
) super.getTrace();
490 public int getVersion() {