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 | ||
23a8deea | 35 | private static final String DISCRIMINATOR = "\\(discriminator.*\\)"; //$NON-NLS-1$ |
522dff53 AM |
36 | private static final String ADDR2LINE_EXECUTABLE = "addr2line"; //$NON-NLS-1$ |
37 | ||
38 | private FileOffsetMapper() {} | |
39 | ||
40 | /** | |
41 | * Generate the callsites from a given binary file and address offset. | |
42 | * | |
43 | * Due to function inlining, it is possible for one offset to actually have | |
44 | * multiple call sites. This is why we can return more than one callsite per | |
45 | * call. | |
46 | * | |
47 | * @param file | |
48 | * The binary file to look at | |
49 | * @param offset | |
50 | * The memory offset in the file | |
51 | * @return The list of callsites corresponding to the offset, reported from | |
52 | * the "highest" inlining location, down to the initial definition. | |
53 | */ | |
54 | public static @Nullable Iterable<TmfCallsite> getCallsiteFromOffset(File file, long offset) { | |
55 | if (!Files.exists((file.toPath()))) { | |
11f39f99 | 56 | return null; |
522dff53 AM |
57 | } |
58 | return getCallsiteFromOffsetWithAddr2line(file, offset); | |
59 | } | |
60 | ||
61 | private static @Nullable Iterable<TmfCallsite> getCallsiteFromOffsetWithAddr2line(File file, long offset) { | |
62 | List<TmfCallsite> callsites = new LinkedList<>(); | |
63 | ||
64 | // FIXME Could eventually use CDT's Addr2line class once it imlements --inlines | |
65 | List<String> output = getOutputFromCommand(checkNotNull(Arrays.asList( | |
66 | ADDR2LINE_EXECUTABLE, "-i", "-e", file.toString(), "0x" + Long.toHexString(offset)))); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ | |
67 | ||
68 | if (output == null) { | |
69 | /* Command returned an error */ | |
70 | return null; | |
71 | } | |
72 | ||
73 | for (String outputLine : output) { | |
91fdda3e MAL |
74 | // Remove discriminator part, for example: /build/buildd/glibc-2.21/elf/dl-object.c:78 (discriminator 8) |
75 | outputLine = outputLine.replaceFirst(DISCRIMINATOR, "").trim(); //$NON-NLS-1$ | |
76 | ||
522dff53 AM |
77 | String[] elems = outputLine.split(":"); //$NON-NLS-1$ |
78 | String fileName = elems[0]; | |
79 | if (fileName.equals("??")) { //$NON-NLS-1$ | |
80 | continue; | |
81 | } | |
82 | long lineNumber = Long.parseLong(elems[1]); | |
83 | ||
84 | callsites.add(new TmfCallsite(fileName, null, lineNumber)); | |
85 | } | |
86 | ||
87 | return callsites; | |
88 | } | |
89 | ||
90 | private static @Nullable List<String> getOutputFromCommand(List<String> command) { | |
91 | try { | |
92 | ProcessBuilder builder = new ProcessBuilder(command); | |
93 | builder.redirectErrorStream(true); | |
94 | ||
95 | Process p = builder.start(); | |
a6c5c267 MK |
96 | try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));) { |
97 | int ret = p.waitFor(); | |
98 | List<String> lines = br.lines().collect(Collectors.toList()); | |
522dff53 | 99 | |
a6c5c267 MK |
100 | return (ret == 0 ? lines : null); |
101 | } | |
522dff53 AM |
102 | } catch (IOException | InterruptedException e) { |
103 | return null; | |
104 | } | |
105 | } | |
106 | } |