blob: 257042b23a0cd7b3582d2e35e9b82a01a1ca3a58 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 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.nullToEmptyString;
import java.io.File;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace;
import org.eclipse.tracecompass.lttng2.ust.core.trace.layout.ILttngUstEventLayout;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/**
* Event aspect of UST traces that indicate the binary callsite (binary, symbol
* and offset) from an IP (instruction pointer) context.
*
* Unlike the {@link UstDebugInfoSourceAspect}, this information should be
* available even without debug information.
*
* @author Alexandre Montplaisir
* @since 2.0
*/
public class UstDebugInfoBinaryAspect implements ITmfEventAspect<BinaryCallsite> {
/** Singleton instance */
public static final UstDebugInfoBinaryAspect INSTANCE = new UstDebugInfoBinaryAspect();
private static final long CACHE_SIZE = 1000;
/**
* Cache of all calls to 'addr2line', so that we can avoid recalling the
* external process repeatedly.
*
* It is static, meaning one cache for the whole application, since the
* symbols in a file on disk are independent from the trace referring to it.
*/
private static final Optional<BinaryCallsite> OPTIONAL_NULL = Objects.requireNonNull(Optional.empty());
private static final LoadingCache<TraceBinarySymbol, Optional<BinaryCallsite>> BINARY_CALLSITE_CACHE;
static {
BINARY_CALLSITE_CACHE = Objects.requireNonNull(CacheBuilder.newBuilder()
.maximumSize(CACHE_SIZE)
.build(new CacheLoader<TraceBinarySymbol, Optional<BinaryCallsite>>() {
@Override
public Optional<BinaryCallsite> load(TraceBinarySymbol symbolIp) {
/*
* First match the IP to the correct binary or library, by using the
* UstDebugInfoAnalysis.
*/
UstDebugInfoAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(symbolIp.fTrace,
UstDebugInfoAnalysisModule.class, UstDebugInfoAnalysisModule.ID);
if (module == null) {
/*
* The analysis is not available for this trace, we won't be able to
* find the information.
*/
return OPTIONAL_NULL;
}
UstDebugInfoLoadedBinaryFile file = module.getMatchingFile(symbolIp.fTs, symbolIp.fPid, symbolIp.fIp);
if (file == null) {
return OPTIONAL_NULL;
}
/* Apply the path prefix defined by the trace, if any */
String fullPath = new File(symbolIp.fTrace.getSymbolProviderConfig().getActualRootDirPath(), file.getFilePath()).toString();
long offset;
if (file.isPic()) {
offset = symbolIp.fIp - file.getBaseAddress();
} else {
/*
* In the case of the object being non-position-independent, we must
* pass the actual 'ip' address directly to addr2line.
*/
offset = symbolIp.fIp;
}
return Objects.requireNonNull(Optional.of(new BinaryCallsite(fullPath, file.getBuildId(), offset, file.isPic(), file.getValidityStart(), file.getValidityEnd())));
}
}));
}
private static final class TraceBinarySymbol {
private LttngUstTrace fTrace;
private int fPid;
private long fTs;
private long fIp;
public TraceBinarySymbol(LttngUstTrace trace, int pid, long ts, long ip) {
fTrace = trace;
fPid = pid;
fTs = ts;
fIp = ip;
}
@Override
public int hashCode() {
// The timestamp does not participate in the hash code, it is for
// the purpose of the cache
return Objects.hash(fTrace, fPid, fIp);
}
@Override
public boolean equals(@Nullable Object obj) {
// The timestamp does not participate in the equality, it is for the
// purpose of the cache
if (!(obj instanceof TraceBinarySymbol)) {
return false;
}
TraceBinarySymbol other = (TraceBinarySymbol) obj;
return Objects.equals(fTrace, other.fTrace) && fPid == other.fPid && fIp == other.fIp;
}
}
private UstDebugInfoBinaryAspect() {
// Do nothing
}
@Override
public String getName() {
return nullToEmptyString(Messages.UstDebugInfoAnalysis_BinaryAspectName);
}
@Override
public String getHelpText() {
return nullToEmptyString(Messages.UstDebugInfoAnalysis_BinaryAspectHelpText);
}
@Override
public @Nullable BinaryCallsite resolve(ITmfEvent event) {
/* This aspect only supports UST traces */
if (!(event.getTrace() instanceof LttngUstTrace)) {
return null;
}
LttngUstTrace trace = (LttngUstTrace) event.getTrace();
ILttngUstEventLayout layout = trace.getEventLayout();
/* We need both the vpid and ip contexts */
ITmfEventField vpidField = event.getContent().getField(layout.contextVpid());
ITmfEventField ipField = event.getContent().getField(layout.contextIp());
if (ipField == null) {
ipField = event.getContent().getField(layout.fieldAddr());
}
if (vpidField == null || ipField == null) {
return null;
}
Long vpid = (Long) vpidField.getValue();
Long ip = (Long) ipField.getValue();
long ts = event.getTimestamp().toNanos();
return getBinaryCallsite(trace, vpid.intValue(), ts, ip.longValue());
}
/**
* Get the binary callsite (which means binary file and offset in this file)
* corresponding to the given instruction pointer, for the given PID and
* timetamp.
*
* @param trace
* The trace, from which we will get the debug info analysis
* @param pid
* The PID for which we want the symbol
* @param ts
* The timestamp of the query
* @param ip
* The instruction pointer address
* @return The {@link BinaryCallsite} object with the relevant information
*/
public static @Nullable BinaryCallsite getBinaryCallsite(LttngUstTrace trace, int pid, long ts, long ip) {
TraceBinarySymbol traceBinarySymbol = new TraceBinarySymbol(trace, pid, ts, ip);
Optional<BinaryCallsite> binaryCallsite = BINARY_CALLSITE_CACHE.getUnchecked(traceBinarySymbol);
if (!binaryCallsite.isPresent()) {
return null;
}
if (!binaryCallsite.get().intersects(ts)) {
BINARY_CALLSITE_CACHE.invalidate(traceBinarySymbol);
binaryCallsite = BINARY_CALLSITE_CACHE.getUnchecked(traceBinarySymbol);
}
return (binaryCallsite.isPresent() ? binaryCallsite.get() : null);
}
/**
* Invalidate all symbols in the cache. This should be called when UST debug
* info configuration changes.
*
* @since 4.1
*/
public static void invalidateSymbolCache() {
// Invalidate all symbols in the cache, even if only one trace has
// changed. It won't happen very often, so it's not a very big
// performance hit compared to the performance gain of the cache
BINARY_CALLSITE_CACHE.invalidateAll();
}
}