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 | ||
522dff53 AM |
12 | import java.io.BufferedReader; |
13 | import java.io.File; | |
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; | |
21 | ||
22 | import org.eclipse.jdt.annotation.Nullable; | |
23 | import org.eclipse.tracecompass.tmf.core.event.lookup.TmfCallsite; | |
24 | ||
25 | /** | |
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}. | |
28 | * | |
29 | * @author Alexandre Montplaisir | |
30 | */ | |
31 | public final class FileOffsetMapper { | |
32 | ||
23a8deea | 33 | private static final String DISCRIMINATOR = "\\(discriminator.*\\)"; //$NON-NLS-1$ |
522dff53 AM |
34 | private static final String ADDR2LINE_EXECUTABLE = "addr2line"; //$NON-NLS-1$ |
35 | ||
36 | private FileOffsetMapper() {} | |
37 | ||
38 | /** | |
39 | * Generate the callsites from a given binary file and address offset. | |
40 | * | |
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 | |
43 | * call. | |
44 | * | |
45 | * @param file | |
46 | * The binary file to look at | |
47 | * @param offset | |
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. | |
51 | */ | |
52 | public static @Nullable Iterable<TmfCallsite> getCallsiteFromOffset(File file, long offset) { | |
53 | if (!Files.exists((file.toPath()))) { | |
11f39f99 | 54 | return null; |
522dff53 AM |
55 | } |
56 | return getCallsiteFromOffsetWithAddr2line(file, offset); | |
57 | } | |
58 | ||
59 | private static @Nullable Iterable<TmfCallsite> getCallsiteFromOffsetWithAddr2line(File file, long offset) { | |
60 | List<TmfCallsite> callsites = new LinkedList<>(); | |
61 | ||
38c5f989 | 62 | // FIXME Could eventually use CDT's Addr2line class once it implements --inlines |
0e4f957e | 63 | List<String> output = getOutputFromCommand(Arrays.asList( |
38c5f989 | 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$ |
522dff53 AM |
65 | |
66 | if (output == null) { | |
67 | /* Command returned an error */ | |
68 | return null; | |
69 | } | |
70 | ||
38c5f989 AM |
71 | /* |
72 | * When passing the -f flag, the output alternates between function | |
73 | * names and file/line location. | |
74 | */ | |
75 | boolean oddLine = true; | |
76 | String currentFunctionName = null; | |
522dff53 | 77 | for (String outputLine : output) { |
91fdda3e MAL |
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$ | |
80 | ||
38c5f989 AM |
81 | if (oddLine) { |
82 | /* This is a line indicating the function name */ | |
83 | currentFunctionName = outputLine; | |
84 | } else { | |
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$ | |
89 | continue; | |
90 | } | |
91 | long lineNumber = Long.parseLong(elems[1]); | |
92 | ||
93 | callsites.add(new TmfCallsite(fileName, currentFunctionName, lineNumber)); | |
522dff53 | 94 | } |
522dff53 | 95 | |
38c5f989 AM |
96 | /* Flip the boolean for the following line */ |
97 | oddLine = !oddLine; | |
522dff53 AM |
98 | } |
99 | ||
100 | return callsites; | |
101 | } | |
102 | ||
103 | private static @Nullable List<String> getOutputFromCommand(List<String> command) { | |
104 | try { | |
105 | ProcessBuilder builder = new ProcessBuilder(command); | |
106 | builder.redirectErrorStream(true); | |
107 | ||
108 | Process p = builder.start(); | |
a6c5c267 MK |
109 | try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));) { |
110 | int ret = p.waitFor(); | |
111 | List<String> lines = br.lines().collect(Collectors.toList()); | |
522dff53 | 112 | |
a6c5c267 MK |
113 | return (ret == 0 ? lines : null); |
114 | } | |
522dff53 AM |
115 | } catch (IOException | InterruptedException e) { |
116 | return null; | |
117 | } | |
118 | } | |
119 | } |