Commit | Line | Data |
---|---|---|
ef7f180d | 1 | /******************************************************************************* |
ed48dc75 | 2 | * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir |
ef7f180d AM |
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.lttng2.ust.core.analysis.debuginfo; | |
11 | ||
12 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; | |
13 | ||
14 | import java.util.Collection; | |
15 | import java.util.Collections; | |
16 | import java.util.List; | |
17 | import java.util.NavigableSet; | |
bbadfd0a | 18 | import java.util.Optional; |
ef7f180d AM |
19 | import java.util.Set; |
20 | import java.util.TreeSet; | |
bbadfd0a | 21 | import java.util.stream.Collectors; |
ef7f180d AM |
22 | |
23 | import org.eclipse.jdt.annotation.Nullable; | |
24 | import org.eclipse.tracecompass.internal.lttng2.ust.core.analysis.debuginfo.UstDebugInfoBinaryFile; | |
522dff53 | 25 | import org.eclipse.tracecompass.internal.lttng2.ust.core.analysis.debuginfo.UstDebugInfoLoadedBinaryFile; |
ef7f180d AM |
26 | import org.eclipse.tracecompass.internal.lttng2.ust.core.analysis.debuginfo.UstDebugInfoStateProvider; |
27 | import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace; | |
28 | import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; | |
29 | import org.eclipse.tracecompass.statesystem.core.StateSystemUtils; | |
30 | import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; | |
31 | import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; | |
32 | import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; | |
33 | import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; | |
58080002 | 34 | import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAbstractAnalysisRequirement; |
ef7f180d AM |
35 | import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException; |
36 | import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; | |
37 | import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; | |
38 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
39 | import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfUtils; | |
40 | ||
ef7f180d | 41 | import com.google.common.collect.ImmutableList; |
ef7f180d AM |
42 | |
43 | /** | |
44 | * Analysis to provide TMF Callsite information by mapping IP (instruction | |
45 | * pointer) contexts to address/line numbers via debug information. | |
46 | * | |
47 | * @author Alexandre Montplaisir | |
48 | * @since 2.0 | |
49 | */ | |
50 | public class UstDebugInfoAnalysisModule extends TmfStateSystemAnalysisModule { | |
51 | ||
52 | /** | |
53 | * Analysis ID, it should match that in the plugin.xml file | |
54 | */ | |
55 | public static final String ID = "org.eclipse.linuxtools.lttng2.ust.analysis.debuginfo"; //$NON-NLS-1$ | |
56 | ||
57 | @Override | |
58 | protected ITmfStateProvider createStateProvider() { | |
59 | return new UstDebugInfoStateProvider(checkNotNull(getTrace())); | |
60 | } | |
61 | ||
62 | @Override | |
63 | public boolean setTrace(ITmfTrace trace) throws TmfAnalysisException { | |
64 | if (!(trace instanceof LttngUstTrace)) { | |
65 | return false; | |
66 | } | |
67 | return super.setTrace(trace); | |
68 | } | |
69 | ||
70 | @Override | |
71 | protected @Nullable LttngUstTrace getTrace() { | |
72 | return (LttngUstTrace) super.getTrace(); | |
73 | } | |
74 | ||
75 | @Override | |
58080002 | 76 | public Iterable<TmfAbstractAnalysisRequirement> getAnalysisRequirements() { |
ef7f180d AM |
77 | // TODO specify actual requirements once the requirement-checking is |
78 | // implemented. This analysis needs "ip" and "vpid" contexts. | |
df2597e0 | 79 | return Collections.EMPTY_SET; |
ef7f180d AM |
80 | } |
81 | ||
82 | @Override | |
83 | public boolean canExecute(ITmfTrace trace) { | |
84 | /* The analysis can only work with LTTng-UST traces... */ | |
85 | if (!(trace instanceof LttngUstTrace)) { | |
86 | return false; | |
87 | } | |
88 | LttngUstTrace ustTrace = (LttngUstTrace) trace; | |
89 | String tracerName = CtfUtils.getTracerName(ustTrace); | |
90 | int majorVersion = CtfUtils.getTracerMajorVersion(ustTrace); | |
91 | int minorVersion = CtfUtils.getTracerMinorVersion(ustTrace); | |
92 | ||
93 | /* ... taken with UST >= 2.8 ... */ | |
94 | if (!LttngUstTrace.TRACER_NAME.equals(tracerName)) { | |
95 | return false; | |
96 | } | |
97 | if (majorVersion < 2) { | |
98 | return false; | |
99 | } | |
100 | if (majorVersion == 2 && minorVersion < 8) { | |
101 | return false; | |
102 | } | |
103 | ||
104 | /* ... that respect the ip/vpid contexts requirements. */ | |
105 | return super.canExecute(trace); | |
106 | } | |
107 | ||
108 | // ------------------------------------------------------------------------ | |
109 | // Class-specific operations | |
110 | // ------------------------------------------------------------------------ | |
111 | ||
112 | /** | |
113 | * Return all the binaries that were detected in the trace. | |
114 | * | |
115 | * @return The binaries (executables or libraries) referred to in the trace. | |
116 | */ | |
117 | public Collection<UstDebugInfoBinaryFile> getAllBinaries() { | |
118 | waitForCompletion(); | |
119 | ITmfStateSystem ss = checkNotNull(getStateSystem()); | |
120 | ||
121 | Set<UstDebugInfoBinaryFile> files = new TreeSet<>(); | |
ed48dc75 PT |
122 | ImmutableList.Builder<Integer> builder = ImmutableList.builder(); |
123 | List<Integer> vpidQuarks = ss.getSubAttributes(-1, false); | |
124 | for (Integer vpidQuark : vpidQuarks) { | |
125 | builder.addAll(ss.getSubAttributes(vpidQuark, false)); | |
126 | } | |
127 | List<Integer> baddrQuarks = builder.build(); | |
ef7f180d | 128 | |
ed48dc75 PT |
129 | /* |
130 | * For each "baddr" attribute, get the "buildId" sub-attribute, | |
131 | * whose value is the file path. | |
132 | */ | |
ef7f180d | 133 | |
ed48dc75 | 134 | for (Integer baddrQuark : baddrQuarks) { |
ef7f180d | 135 | |
ed48dc75 PT |
136 | List<Integer> buildIdQuarks = ss.getSubAttributes(baddrQuark, false); |
137 | for (Integer buildIdQuark : buildIdQuarks) { | |
138 | String buildId = ss.getAttributeName(buildIdQuark); | |
ef7f180d | 139 | |
ed48dc75 PT |
140 | /* |
141 | * Explore the history of this attribute "horizontally", | |
142 | * even though there should only be one valid interval. | |
143 | */ | |
144 | ITmfStateInterval interval = StateSystemUtils.queryUntilNonNullValue(ss, buildIdQuark, ss.getStartTime(), Long.MAX_VALUE); | |
145 | if (interval == null) { | |
ef7f180d | 146 | /* |
ed48dc75 PT |
147 | * If we created the attribute, we should have assigned |
148 | * a value to it! | |
ef7f180d | 149 | */ |
ed48dc75 | 150 | throw new IllegalStateException(); |
ef7f180d | 151 | } |
ed48dc75 PT |
152 | String filePath = interval.getStateValue().unboxStr(); |
153 | ||
154 | files.add(new UstDebugInfoBinaryFile(filePath, buildId)); | |
ef7f180d | 155 | } |
ef7f180d AM |
156 | } |
157 | return files; | |
158 | } | |
159 | ||
160 | /** | |
161 | * Get the binary file (executable or library) that corresponds to a given | |
162 | * instruction pointer, at a given time. | |
163 | * | |
164 | * @param ts | |
165 | * The timestamp | |
166 | * @param vpid | |
167 | * The VPID of the process we are querying for | |
168 | * @param ip | |
169 | * The instruction pointer of the trace event. Normally comes | |
170 | * from a 'ip' context. | |
171 | * @return The {@link UstDebugInfoBinaryFile} object, containing both the binary's path | |
172 | * and its build ID. | |
173 | */ | |
522dff53 | 174 | @Nullable UstDebugInfoLoadedBinaryFile getMatchingFile(long ts, long vpid, long ip) { |
ef7f180d AM |
175 | waitForCompletion(); |
176 | final ITmfStateSystem ss = checkNotNull(getStateSystem()); | |
177 | ||
178 | List<Integer> possibleBaddrQuarks = ss.getQuarks(String.valueOf(vpid), "*"); //$NON-NLS-1$ | |
179 | ||
180 | /* Get the most probable base address from all the known ones */ | |
bbadfd0a AM |
181 | NavigableSet<Long> possibleBaddrs = possibleBaddrQuarks.stream() |
182 | .map(quark -> { | |
0e4f957e | 183 | String baddrStr = ss.getAttributeName(quark.intValue()); |
bbadfd0a AM |
184 | return checkNotNull(Long.valueOf(baddrStr)); |
185 | }) | |
186 | .collect(Collectors.toCollection(TreeSet::new)); | |
187 | ||
ef7f180d | 188 | final Long potentialBaddr = possibleBaddrs.floor(ip); |
5406cf6c MAL |
189 | if (potentialBaddr == null) { |
190 | return null; | |
191 | } | |
ef7f180d AM |
192 | |
193 | /* Make sure the 'ip' fits in the expected memory range */ | |
194 | try { | |
195 | final List<ITmfStateInterval> fullState = ss.queryFullState(ts); | |
196 | ||
197 | final int baddrQuark = ss.getQuarkAbsolute(String.valueOf(vpid), String.valueOf(potentialBaddr)); | |
198 | final long endAddr = fullState.get(baddrQuark).getStateValue().unboxLong(); | |
199 | ||
200 | if (!(ip < endAddr)) { | |
201 | /* | |
202 | * Not the correct memory range after all. We do not have | |
203 | * information about the library that was loaded here. | |
204 | */ | |
205 | return null; | |
206 | } | |
207 | ||
208 | /* | |
209 | * We've found the correct base address, now to determine what | |
210 | * library was loaded there at that time. | |
211 | */ | |
212 | List<Integer> buildIds = ss.getSubAttributes(baddrQuark, false); | |
bbadfd0a AM |
213 | Optional<Integer> potentialBuildIdQuark = buildIds.stream() |
214 | .filter(id -> { | |
0e4f957e | 215 | int quark = id.intValue(); |
ef7f180d AM |
216 | ITmfStateValue value = fullState.get(quark).getStateValue(); |
217 | return (!value.isNull()); | |
bbadfd0a AM |
218 | }) |
219 | .findFirst(); | |
ef7f180d AM |
220 | |
221 | if (!potentialBuildIdQuark.isPresent()) { | |
222 | /* We didn't have the information after all. */ | |
223 | return null; | |
224 | } | |
225 | ||
226 | /* Ok, we have everything we need! Return the information. */ | |
522dff53 AM |
227 | long baddr = Long.parseLong(ss.getAttributeName(baddrQuark)); |
228 | ||
ef7f180d AM |
229 | int buildIdQuark = potentialBuildIdQuark.get().intValue(); |
230 | String buildId = ss.getAttributeName(buildIdQuark); | |
231 | String filePath = fullState.get(buildIdQuark).getStateValue().unboxStr(); | |
522dff53 | 232 | return new UstDebugInfoLoadedBinaryFile(baddr, filePath, buildId); |
ef7f180d AM |
233 | |
234 | } catch (AttributeNotFoundException e) { | |
235 | /* We're only using quarks we've checked for. */ | |
236 | throw new IllegalStateException(e); | |
237 | } catch (StateSystemDisposedException e) { | |
238 | return null; | |
239 | } | |
240 | ||
241 | } | |
ef7f180d | 242 | } |