1 /*******************************************************************************
2 * Copyright (c) 2015 EfficiOS Inc., Alexandre Montplaisir
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which 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 java
.io
.BufferedReader
;
14 import java
.io
.IOException
;
15 import java
.io
.InputStreamReader
;
16 import java
.nio
.file
.Files
;
17 import java
.util
.Arrays
;
18 import java
.util
.LinkedList
;
19 import java
.util
.List
;
20 import java
.util
.stream
.Collectors
;
22 import org
.eclipse
.jdt
.annotation
.Nullable
;
23 import org
.eclipse
.tracecompass
.tmf
.core
.event
.lookup
.TmfCallsite
;
26 * Utility class to get file name, function/symbol name and line number from a
27 * given offset. In TMF this is represented as a {@link TmfCallsite}.
29 * @author Alexandre Montplaisir
31 public final class FileOffsetMapper
{
33 private static final String DISCRIMINATOR
= "\\(discriminator.*\\)"; //$NON-NLS-1$
34 private static final String ADDR2LINE_EXECUTABLE
= "addr2line"; //$NON-NLS-1$
36 private FileOffsetMapper() {}
39 * Generate the callsites from a given binary file and address offset.
41 * Due to function inlining, it is possible for one offset to actually have
42 * multiple call sites. This is why we can return more than one callsite per
46 * The binary file to look at
48 * The memory offset in the file
49 * @return The list of callsites corresponding to the offset, reported from
50 * the "highest" inlining location, down to the initial definition.
52 public static @Nullable Iterable
<TmfCallsite
> getCallsiteFromOffset(File file
, long offset
) {
53 if (!Files
.exists((file
.toPath()))) {
56 return getCallsiteFromOffsetWithAddr2line(file
, offset
);
59 private static @Nullable Iterable
<TmfCallsite
> getCallsiteFromOffsetWithAddr2line(File file
, long offset
) {
60 List
<TmfCallsite
> callsites
= new LinkedList
<>();
62 // FIXME Could eventually use CDT's Addr2line class once it implements --inlines
63 List
<String
> output
= getOutputFromCommand(Arrays
.asList(
64 ADDR2LINE_EXECUTABLE
, "-i", "-f", "-C", "-e", file
.toString(), "0x" + Long
.toHexString(offset
))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
67 /* Command returned an error */
72 * When passing the -f flag, the output alternates between function
73 * names and file/line location.
75 boolean oddLine
= true;
76 String currentFunctionName
= null;
77 for (String outputLine
: output
) {
78 // Remove discriminator part, for example: /build/buildd/glibc-2.21/elf/dl-object.c:78 (discriminator 8)
79 outputLine
= outputLine
.replaceFirst(DISCRIMINATOR
, "").trim(); //$NON-NLS-1$
82 /* This is a line indicating the function name */
83 currentFunctionName
= outputLine
;
85 /* This is a line indicating a call site */
86 String
[] elems
= outputLine
.split(":"); //$NON-NLS-1$
87 String fileName
= elems
[0];
88 if (fileName
.equals("??")) { //$NON-NLS-1$
91 long lineNumber
= Long
.parseLong(elems
[1]);
93 callsites
.add(new TmfCallsite(fileName
, currentFunctionName
, lineNumber
));
96 /* Flip the boolean for the following line */
103 private static @Nullable List
<String
> getOutputFromCommand(List
<String
> command
) {
105 ProcessBuilder builder
= new ProcessBuilder(command
);
106 builder
.redirectErrorStream(true);
108 Process p
= builder
.start();
109 try (BufferedReader br
= new BufferedReader(new InputStreamReader(p
.getInputStream()));) {
110 int ret
= p
.waitFor();
111 List
<String
> lines
= br
.lines().collect(Collectors
.toList());
113 return (ret
== 0 ? lines
: null);
115 } catch (IOException
| InterruptedException e
) {