| /******************************************************************************* |
| * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.lttng2.ust.core.analysis.debuginfo; |
| |
| import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.OptionalLong; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.internal.lttng2.ust.core.analysis.debuginfo.UstDebugInfoStateProvider; |
| import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace; |
| import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; |
| import org.eclipse.tracecompass.statesystem.core.StateSystemUtils; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; |
| import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; |
| import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; |
| import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAbstractAnalysisRequirement; |
| import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException; |
| import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; |
| import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfUtils; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableList; |
| |
| /** |
| * Analysis to provide TMF Callsite information by mapping IP (instruction |
| * pointer) contexts to address/line numbers via debug information. |
| * |
| * @author Alexandre Montplaisir |
| * @since 2.0 |
| */ |
| public class UstDebugInfoAnalysisModule extends TmfStateSystemAnalysisModule { |
| |
| /** |
| * Analysis ID, it should match that in the plugin.xml file |
| */ |
| public static final String ID = "org.eclipse.linuxtools.lttng2.ust.analysis.debuginfo"; //$NON-NLS-1$ |
| |
| @Override |
| protected ITmfStateProvider createStateProvider() { |
| return new UstDebugInfoStateProvider(checkNotNull(getTrace())); |
| } |
| |
| @Override |
| public boolean setTrace(ITmfTrace trace) throws TmfAnalysisException { |
| if (!(trace instanceof LttngUstTrace)) { |
| return false; |
| } |
| return super.setTrace(trace); |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| @Override |
| @Nullable |
| public LttngUstTrace getTrace() { |
| return (LttngUstTrace) super.getTrace(); |
| } |
| |
| @Override |
| public Iterable<TmfAbstractAnalysisRequirement> getAnalysisRequirements() { |
| // TODO specify actual requirements once the requirement-checking is |
| // implemented. This analysis needs "ip" and "vpid" contexts. |
| return Collections.emptySet(); |
| } |
| |
| @Override |
| public boolean canExecute(ITmfTrace trace) { |
| /* The analysis can only work with LTTng-UST traces... */ |
| if (!(trace instanceof LttngUstTrace)) { |
| return false; |
| } |
| LttngUstTrace ustTrace = (LttngUstTrace) trace; |
| String tracerName = CtfUtils.getTracerName(ustTrace); |
| int majorVersion = CtfUtils.getTracerMajorVersion(ustTrace); |
| int minorVersion = CtfUtils.getTracerMinorVersion(ustTrace); |
| |
| /* ... taken with UST >= 2.8 ... */ |
| if (!LttngUstTrace.TRACER_NAME.equals(tracerName)) { |
| return false; |
| } |
| if (majorVersion < 2) { |
| return false; |
| } |
| if (majorVersion == 2 && minorVersion < 8) { |
| return false; |
| } |
| |
| /* ... that respect the ip/vpid contexts requirements. */ |
| return super.canExecute(trace); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Class-specific operations |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Return all the binaries that were detected in the trace. |
| * |
| * @return The binaries (executables or libraries) referred to in the trace. |
| */ |
| public Collection<UstDebugInfoBinaryFile> getAllBinaries() { |
| ITmfStateSystem ss = getStateSystem(); |
| if (ss == null) { |
| /* State system might not yet be initialized */ |
| return Collections.emptySet(); |
| } |
| |
| final @NonNull Set<UstDebugInfoBinaryFile> files = new TreeSet<>(); |
| ImmutableList.Builder<Integer> builder = ImmutableList.builder(); |
| List<Integer> vpidQuarks = ss.getSubAttributes(ITmfStateSystem.ROOT_ATTRIBUTE, false); |
| for (Integer vpidQuark : vpidQuarks) { |
| builder.addAll(ss.getSubAttributes(vpidQuark, false)); |
| } |
| List<Integer> baddrQuarks = builder.build(); |
| |
| try { |
| for (Integer baddrQuark : baddrQuarks) { |
| int buildIdQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.BUILD_ID_ATTRIB); |
| int debugLinkQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.DEBUG_LINK_ATTRIB); |
| int pathQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.PATH_ATTRIB); |
| int isPICQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.IS_PIC_ATTRIB); |
| long ts = ss.getStartTime(); |
| |
| /* |
| * Iterate over each mapping there ever was at this base |
| * address. |
| */ |
| ITmfStateInterval interval = StateSystemUtils.queryUntilNonNullValue(ss, baddrQuark, ts, Long.MAX_VALUE); |
| while (interval != null) { |
| ts = interval.getStartTime(); |
| |
| ITmfStateValue filePathStateValue = ss.querySingleState(ts, pathQuark).getStateValue(); |
| String filePath = filePathStateValue.unboxStr(); |
| |
| ITmfStateValue buildIdStateValue = ss.querySingleState(ts, buildIdQuark).getStateValue(); |
| String buildId = unboxStrOrNull(buildIdStateValue); |
| |
| ITmfStateValue debuglinkStateValue = ss.querySingleState(ts, debugLinkQuark).getStateValue(); |
| String debugLink = unboxStrOrNull(debuglinkStateValue); |
| |
| ITmfStateValue isPICStateValue = ss.querySingleState(ts, isPICQuark).getStateValue(); |
| Boolean isPIC = isPICStateValue.unboxInt() != 0; |
| |
| files.add(new UstDebugInfoBinaryFile(filePath, buildId, debugLink, isPIC)); |
| |
| /* |
| * Go one past the end of the interval, and perform the |
| * query again to find the next mapping at this address. |
| */ |
| ts = interval.getEndTime() + 1; |
| interval = StateSystemUtils.queryUntilNonNullValue(ss, baddrQuark, ts, Long.MAX_VALUE); |
| } |
| } |
| } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException e) { |
| /* Oh well, such is life. */ |
| } |
| return files; |
| } |
| |
| /** |
| * Get the binary file (executable or library) that corresponds to a given |
| * instruction pointer, at a given time. |
| * |
| * @param ts |
| * The timestamp |
| * @param vpid |
| * The VPID of the process we are querying for |
| * @param ip |
| * The instruction pointer of the trace event. Normally comes |
| * from a 'ip' context. |
| * @return A {@link UstDebugInfoLoadedBinaryFile} object, describing the |
| * binary file and its base address. |
| * @noreference Meant to be used internally by |
| * {@link UstDebugInfoBinaryAspect} only. |
| */ |
| @VisibleForTesting |
| public @Nullable UstDebugInfoLoadedBinaryFile getMatchingFile(long ts, long vpid, long ip) { |
| try { |
| final ITmfStateSystem ss = getStateSystem(); |
| if (ss == null) { |
| /* State system might not yet be initialized */ |
| return null; |
| } |
| |
| List<Integer> possibleBaddrQuarks = ss.getQuarks(String.valueOf(vpid), "*"); //$NON-NLS-1$ |
| List<ITmfStateInterval> state = ss.queryFullState(ts); |
| |
| /* Get the most probable base address from all the known ones */ |
| OptionalLong potentialBaddr = possibleBaddrQuarks.stream() |
| /* Keep only currently (at ts) mapped objects. */ |
| .filter(quark -> Objects.equals(1, state.get(quark).getValue())) |
| .map(ss::getAttributeName) |
| .mapToLong(Long::parseLong) |
| .filter(baddr -> baddr <= ip) |
| .max(); |
| |
| if (!potentialBaddr.isPresent()) { |
| return null; |
| } |
| |
| long baddr = potentialBaddr.getAsLong(); |
| final int baddrQuark = ss.getQuarkAbsolute(String.valueOf(vpid), |
| String.valueOf(baddr)); |
| |
| final int memszQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.MEMSZ_ATTRIB); |
| final long memsz = state.get(memszQuark).getStateValue().unboxLong(); |
| |
| /* Make sure the 'ip' fits the range of this object. */ |
| if (!(ip < baddr + memsz)) { |
| /* |
| * Not the correct memory range after all. We do not have |
| * information about the library that was loaded here. |
| */ |
| return null; |
| } |
| |
| final int pathQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.PATH_ATTRIB); |
| String filePath = state.get(pathQuark).getStateValue().unboxStr(); |
| |
| final int buildIdQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.BUILD_ID_ATTRIB); |
| ITmfStateValue buildIdValue = state.get(buildIdQuark).getStateValue(); |
| String buildId = unboxStrOrNull(buildIdValue); |
| |
| final int debugLinkQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.DEBUG_LINK_ATTRIB); |
| ITmfStateValue debugLinkValue = state.get(debugLinkQuark).getStateValue(); |
| String debugLink = unboxStrOrNull(debugLinkValue); |
| |
| final int isPicQuark = ss.getQuarkRelative(baddrQuark, UstDebugInfoStateProvider.IS_PIC_ATTRIB); |
| boolean isPic = state.get(isPicQuark).getStateValue().unboxInt() != 0; |
| |
| // The baddrQuark interval lasts for the time this file is loaded |
| ITmfStateInterval validityInterval = state.get(baddrQuark); |
| |
| return new UstDebugInfoLoadedBinaryFile(baddr, filePath, buildId, debugLink, isPic, validityInterval.getStartTime(), validityInterval.getEndTime()); |
| |
| } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException e) { |
| /* Either the data is not available yet, or incomplete. */ |
| return null; |
| } |
| } |
| |
| private static @Nullable String unboxStrOrNull(ITmfStateValue value) { |
| return (value.isNull() ? null : value.unboxStr()); |
| } |
| } |