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 | ||
62 | // FIXME Could eventually use CDT's Addr2line class once it imlements --inlines | |
0e4f957e AM |
63 | List<String> output = getOutputFromCommand(Arrays.asList( |
64 | ADDR2LINE_EXECUTABLE, "-i", "-e", file.toString(), "0x" + Long.toHexString(offset))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ | |
522dff53 AM |
65 | |
66 | if (output == null) { | |
67 | /* Command returned an error */ | |
68 | return null; | |
69 | } | |
70 | ||
71 | for (String outputLine : output) { | |
91fdda3e MAL |
72 | // Remove discriminator part, for example: /build/buildd/glibc-2.21/elf/dl-object.c:78 (discriminator 8) |
73 | outputLine = outputLine.replaceFirst(DISCRIMINATOR, "").trim(); //$NON-NLS-1$ | |
74 | ||
522dff53 AM |
75 | String[] elems = outputLine.split(":"); //$NON-NLS-1$ |
76 | String fileName = elems[0]; | |
77 | if (fileName.equals("??")) { //$NON-NLS-1$ | |
78 | continue; | |
79 | } | |
80 | long lineNumber = Long.parseLong(elems[1]); | |
81 | ||
82 | callsites.add(new TmfCallsite(fileName, null, lineNumber)); | |
83 | } | |
84 | ||
85 | return callsites; | |
86 | } | |
87 | ||
88 | private static @Nullable List<String> getOutputFromCommand(List<String> command) { | |
89 | try { | |
90 | ProcessBuilder builder = new ProcessBuilder(command); | |
91 | builder.redirectErrorStream(true); | |
92 | ||
93 | Process p = builder.start(); | |
a6c5c267 MK |
94 | try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));) { |
95 | int ret = p.waitFor(); | |
96 | List<String> lines = br.lines().collect(Collectors.toList()); | |
522dff53 | 97 | |
a6c5c267 MK |
98 | return (ret == 0 ? lines : null); |
99 | } | |
522dff53 AM |
100 | } catch (IOException | InterruptedException e) { |
101 | return null; | |
102 | } | |
103 | } | |
104 | } |