Commit | Line | Data |
---|---|---|
ef7f180d 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.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; | |
34 | import org.eclipse.tracecompass.tmf.core.analysis.TmfAnalysisRequirement; | |
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 | |
76 | public Iterable<TmfAnalysisRequirement> getAnalysisRequirements() { | |
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<>(); | |
122 | try { | |
123 | ImmutableList.Builder<Integer> builder = ImmutableList.builder(); | |
124 | List<Integer> vpidQuarks = ss.getSubAttributes(-1, false); | |
125 | for (Integer vpidQuark : vpidQuarks) { | |
126 | builder.addAll(ss.getSubAttributes(vpidQuark, false)); | |
127 | } | |
128 | List<Integer> baddrQuarks = builder.build(); | |
129 | ||
130 | /* | |
131 | * For each "baddr" attribute, get the "buildId" sub-attribute, | |
132 | * whose value is the file path. | |
133 | */ | |
134 | ||
135 | for (Integer baddrQuark : baddrQuarks) { | |
136 | ||
137 | List<Integer> buildIdQuarks = ss.getSubAttributes(baddrQuark, false); | |
138 | for (Integer buildIdQuark : buildIdQuarks) { | |
139 | String buildId = ss.getAttributeName(buildIdQuark); | |
140 | ||
141 | /* | |
142 | * Explore the history of this attribute "horizontally", | |
143 | * even though there should only be one valid interval. | |
144 | */ | |
145 | ITmfStateInterval interval = StateSystemUtils.queryUntilNonNullValue(ss, buildIdQuark, ss.getStartTime(), Long.MAX_VALUE); | |
146 | if (interval == null) { | |
147 | /* | |
148 | * If we created the attribute, we should have assigned | |
149 | * a value to it! | |
150 | */ | |
151 | throw new IllegalStateException(); | |
152 | } | |
153 | String filePath = interval.getStateValue().unboxStr(); | |
154 | ||
155 | files.add(new UstDebugInfoBinaryFile(filePath, buildId)); | |
156 | } | |
157 | } | |
158 | } catch (AttributeNotFoundException e) { | |
159 | throw new IllegalStateException(e); | |
160 | } | |
161 | return files; | |
162 | } | |
163 | ||
164 | /** | |
165 | * Get the binary file (executable or library) that corresponds to a given | |
166 | * instruction pointer, at a given time. | |
167 | * | |
168 | * @param ts | |
169 | * The timestamp | |
170 | * @param vpid | |
171 | * The VPID of the process we are querying for | |
172 | * @param ip | |
173 | * The instruction pointer of the trace event. Normally comes | |
174 | * from a 'ip' context. | |
175 | * @return The {@link UstDebugInfoBinaryFile} object, containing both the binary's path | |
176 | * and its build ID. | |
177 | */ | |
522dff53 | 178 | @Nullable UstDebugInfoLoadedBinaryFile getMatchingFile(long ts, long vpid, long ip) { |
ef7f180d AM |
179 | waitForCompletion(); |
180 | final ITmfStateSystem ss = checkNotNull(getStateSystem()); | |
181 | ||
182 | List<Integer> possibleBaddrQuarks = ss.getQuarks(String.valueOf(vpid), "*"); //$NON-NLS-1$ | |
183 | ||
184 | /* Get the most probable base address from all the known ones */ | |
bbadfd0a AM |
185 | NavigableSet<Long> possibleBaddrs = possibleBaddrQuarks.stream() |
186 | .map(quark -> { | |
0e4f957e | 187 | String baddrStr = ss.getAttributeName(quark.intValue()); |
bbadfd0a AM |
188 | return checkNotNull(Long.valueOf(baddrStr)); |
189 | }) | |
190 | .collect(Collectors.toCollection(TreeSet::new)); | |
191 | ||
ef7f180d | 192 | final Long potentialBaddr = possibleBaddrs.floor(ip); |
5406cf6c MAL |
193 | if (potentialBaddr == null) { |
194 | return null; | |
195 | } | |
ef7f180d AM |
196 | |
197 | /* Make sure the 'ip' fits in the expected memory range */ | |
198 | try { | |
199 | final List<ITmfStateInterval> fullState = ss.queryFullState(ts); | |
200 | ||
201 | final int baddrQuark = ss.getQuarkAbsolute(String.valueOf(vpid), String.valueOf(potentialBaddr)); | |
202 | final long endAddr = fullState.get(baddrQuark).getStateValue().unboxLong(); | |
203 | ||
204 | if (!(ip < endAddr)) { | |
205 | /* | |
206 | * Not the correct memory range after all. We do not have | |
207 | * information about the library that was loaded here. | |
208 | */ | |
209 | return null; | |
210 | } | |
211 | ||
212 | /* | |
213 | * We've found the correct base address, now to determine what | |
214 | * library was loaded there at that time. | |
215 | */ | |
216 | List<Integer> buildIds = ss.getSubAttributes(baddrQuark, false); | |
bbadfd0a AM |
217 | Optional<Integer> potentialBuildIdQuark = buildIds.stream() |
218 | .filter(id -> { | |
0e4f957e | 219 | int quark = id.intValue(); |
ef7f180d AM |
220 | ITmfStateValue value = fullState.get(quark).getStateValue(); |
221 | return (!value.isNull()); | |
bbadfd0a AM |
222 | }) |
223 | .findFirst(); | |
ef7f180d AM |
224 | |
225 | if (!potentialBuildIdQuark.isPresent()) { | |
226 | /* We didn't have the information after all. */ | |
227 | return null; | |
228 | } | |
229 | ||
230 | /* Ok, we have everything we need! Return the information. */ | |
522dff53 AM |
231 | long baddr = Long.parseLong(ss.getAttributeName(baddrQuark)); |
232 | ||
ef7f180d AM |
233 | int buildIdQuark = potentialBuildIdQuark.get().intValue(); |
234 | String buildId = ss.getAttributeName(buildIdQuark); | |
235 | String filePath = fullState.get(buildIdQuark).getStateValue().unboxStr(); | |
522dff53 | 236 | return new UstDebugInfoLoadedBinaryFile(baddr, filePath, buildId); |
ef7f180d AM |
237 | |
238 | } catch (AttributeNotFoundException e) { | |
239 | /* We're only using quarks we've checked for. */ | |
240 | throw new IllegalStateException(e); | |
241 | } catch (StateSystemDisposedException e) { | |
242 | return null; | |
243 | } | |
244 | ||
245 | } | |
ef7f180d | 246 | } |