Commit | Line | Data |
---|---|---|
522dff53 AM |
1 | /******************************************************************************* |
2 | * Copyright (c) 2015 EfficiOS Inc., Alexandre Montplaisir | |
3 | * | |
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 | *******************************************************************************/ | |
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.io.BufferedReader; | |
15 | import java.io.File; | |
16 | import java.io.IOException; | |
17 | import java.io.InputStreamReader; | |
18 | import java.nio.file.Files; | |
19 | import java.util.Arrays; | |
20 | import java.util.LinkedList; | |
21 | import java.util.List; | |
22 | import java.util.stream.Collectors; | |
23 | ||
24 | import org.eclipse.jdt.annotation.Nullable; | |
25 | import org.eclipse.tracecompass.tmf.core.event.lookup.TmfCallsite; | |
26 | ||
27 | /** | |
28 | * Utility class to get file name, function/symbol name and line number from a | |
29 | * given offset. In TMF this is represented as a {@link TmfCallsite}. | |
30 | * | |
31 | * @author Alexandre Montplaisir | |
32 | */ | |
33 | public final class FileOffsetMapper { | |
34 | ||
35 | private static final String ADDR2LINE_EXECUTABLE = "addr2line"; //$NON-NLS-1$ | |
36 | ||
37 | private FileOffsetMapper() {} | |
38 | ||
39 | /** | |
40 | * Generate the callsites from a given binary file and address offset. | |
41 | * | |
42 | * Due to function inlining, it is possible for one offset to actually have | |
43 | * multiple call sites. This is why we can return more than one callsite per | |
44 | * call. | |
45 | * | |
46 | * @param file | |
47 | * The binary file to look at | |
48 | * @param offset | |
49 | * The memory offset in the file | |
50 | * @return The list of callsites corresponding to the offset, reported from | |
51 | * the "highest" inlining location, down to the initial definition. | |
52 | */ | |
53 | public static @Nullable Iterable<TmfCallsite> getCallsiteFromOffset(File file, long offset) { | |
54 | if (!Files.exists((file.toPath()))) { | |
55 | throw new IllegalArgumentException(); | |
56 | } | |
57 | return getCallsiteFromOffsetWithAddr2line(file, offset); | |
58 | } | |
59 | ||
60 | private static @Nullable Iterable<TmfCallsite> getCallsiteFromOffsetWithAddr2line(File file, long offset) { | |
61 | List<TmfCallsite> callsites = new LinkedList<>(); | |
62 | ||
63 | // FIXME Could eventually use CDT's Addr2line class once it imlements --inlines | |
64 | List<String> output = getOutputFromCommand(checkNotNull(Arrays.asList( | |
65 | ADDR2LINE_EXECUTABLE, "-i", "-e", file.toString(), "0x" + Long.toHexString(offset)))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ | |
66 | ||
67 | if (output == null) { | |
68 | /* Command returned an error */ | |
69 | return null; | |
70 | } | |
71 | ||
72 | for (String outputLine : output) { | |
73 | String[] elems = outputLine.split(":"); //$NON-NLS-1$ | |
74 | String fileName = elems[0]; | |
75 | if (fileName.equals("??")) { //$NON-NLS-1$ | |
76 | continue; | |
77 | } | |
78 | long lineNumber = Long.parseLong(elems[1]); | |
79 | ||
80 | callsites.add(new TmfCallsite(fileName, null, lineNumber)); | |
81 | } | |
82 | ||
83 | return callsites; | |
84 | } | |
85 | ||
86 | private static @Nullable List<String> getOutputFromCommand(List<String> command) { | |
87 | try { | |
88 | ProcessBuilder builder = new ProcessBuilder(command); | |
89 | builder.redirectErrorStream(true); | |
90 | ||
91 | Process p = builder.start(); | |
92 | BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); | |
93 | int ret = p.waitFor(); | |
94 | List<String> lines = br.lines().collect(Collectors.toList()); | |
95 | ||
96 | return (ret == 0 ? lines : null); | |
97 | ||
98 | } catch (IOException | InterruptedException e) { | |
99 | return null; | |
100 | } | |
101 | } | |
102 | } |