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