Commit | Line | Data |
---|---|---|
af7f72ce FG |
1 | /******************************************************************************* |
2 | * Copyright (c) 2015 École Polytechnique de Montréal | |
3 | * | |
4 | * All rights reserved. This program and the accompanying materials are | |
5 | * made available under the terms of the Eclipse Public License v1.0 which | |
6 | * 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.kernel.core.analysis.graph.handlers; | |
11 | ||
12 | import java.util.Collection; | |
13 | import java.util.Collections; | |
14 | import java.util.HashMap; | |
4c4e2816 | 15 | import java.util.Map; |
af7f72ce FG |
16 | |
17 | import org.eclipse.core.runtime.NullProgressMonitor; | |
18 | import org.eclipse.jdt.annotation.Nullable; | |
19 | import org.eclipse.tracecompass.analysis.graph.core.base.TmfEdge; | |
20 | import org.eclipse.tracecompass.analysis.graph.core.base.TmfEdge.EdgeType; | |
21 | import org.eclipse.tracecompass.analysis.graph.core.base.TmfGraph; | |
22 | import org.eclipse.tracecompass.analysis.graph.core.base.TmfVertex; | |
23 | import org.eclipse.tracecompass.analysis.graph.core.base.TmfVertex.EdgeDirection; | |
0f7a12d3 | 24 | import org.eclipse.tracecompass.analysis.os.linux.core.kernel.LinuxValues; |
af7f72ce | 25 | import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread; |
5aa7fe19 | 26 | import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout; |
af7f72ce FG |
27 | import org.eclipse.tracecompass.common.core.NonNullUtils; |
28 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.TcpEventStrings; | |
29 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.graph.building.LttngKernelExecGraphProvider; | |
30 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.graph.building.LttngKernelExecGraphProvider.Context; | |
31 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.graph.building.LttngKernelExecGraphProvider.ProcessStatus; | |
32 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.graph.model.EventField; | |
33 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.graph.model.LttngInterruptContext; | |
34 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.graph.model.LttngSystemModel; | |
35 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.graph.model.LttngWorker; | |
af7f72ce FG |
36 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
37 | import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect; | |
38 | import org.eclipse.tracecompass.tmf.core.event.matching.IMatchProcessingUnit; | |
39 | import org.eclipse.tracecompass.tmf.core.event.matching.TmfEventDependency; | |
40 | import org.eclipse.tracecompass.tmf.core.event.matching.TmfEventMatching; | |
41 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
42 | import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; | |
43 | ||
44 | import com.google.common.collect.HashBasedTable; | |
45 | import com.google.common.collect.Table; | |
46 | ||
47 | /** | |
48 | * Event handler that actually builds the execution graph from the events | |
49 | * | |
50 | * @author Francis Giraldeau | |
51 | * @author Geneviève Bastien | |
52 | */ | |
53 | public class TraceEventHandlerExecutionGraph extends BaseHandler { | |
54 | ||
55 | /* | |
56 | * The following IRQ constants was found empirically. | |
57 | * | |
58 | * TODO: other IRQ values should be determined from the lttng_statedump_interrupt events. | |
59 | */ | |
60 | private static final int IRQ_TIMER = 0; | |
61 | ||
62 | private static final NullProgressMonitor DEFAULT_PROGRESS_MONITOR = new NullProgressMonitor(); | |
63 | ||
64 | private final Table<String, Integer, LttngWorker> fKernel; | |
65 | private final IMatchProcessingUnit fMatchProcessing; | |
4c4e2816 | 66 | private Map<ITmfEvent, TmfVertex> fTcpNodes; |
af7f72ce FG |
67 | private TmfEventMatching fTcpMatching; |
68 | ||
69 | /** | |
70 | * Constructor | |
71 | * | |
72 | * @param provider | |
73 | * The parent graph provider | |
74 | */ | |
75 | public TraceEventHandlerExecutionGraph(LttngKernelExecGraphProvider provider) { | |
76 | super(provider); | |
dc303fab | 77 | fKernel = HashBasedTable.create(); |
af7f72ce FG |
78 | |
79 | fTcpNodes = new HashMap<>(); | |
80 | fMatchProcessing = new IMatchProcessingUnit() { | |
81 | ||
82 | @Override | |
83 | public void matchingEnded() { | |
84 | } | |
85 | ||
86 | @Override | |
87 | public int countMatches() { | |
88 | return 0; | |
89 | } | |
90 | ||
91 | @Override | |
92 | public void addMatch(@Nullable TmfEventDependency match) { | |
93 | if (match == null) { | |
94 | return; | |
95 | } | |
96 | TmfVertex output = fTcpNodes.remove(match.getSourceEvent()); | |
97 | TmfVertex input = fTcpNodes.remove(match.getDestinationEvent()); | |
98 | if (output != null && input != null) { | |
99 | output.linkVertical(input).setType(EdgeType.NETWORK); | |
100 | } | |
101 | } | |
102 | ||
103 | @Override | |
4c4e2816 | 104 | public void init(Collection<ITmfTrace> fTraces) { |
af7f72ce FG |
105 | |
106 | } | |
107 | ||
108 | }; | |
109 | ||
110 | ITmfTrace trace = provider.getTrace(); | |
111 | fTcpMatching = new TmfEventMatching(Collections.singleton(trace), fMatchProcessing); | |
112 | fTcpMatching.initMatching(); | |
113 | } | |
114 | ||
115 | private LttngWorker getOrCreateKernelWorker(ITmfEvent event, Integer cpu) { | |
116 | String host = event.getTrace().getHostId(); | |
117 | LttngWorker worker = fKernel.get(host, cpu); | |
118 | if (worker == null) { | |
119 | HostThread ht = new HostThread(host, -1); | |
120 | worker = new LttngWorker(ht, "kernel/" + cpu, event.getTimestamp().getValue()); //$NON-NLS-1$ | |
121 | worker.setStatus(ProcessStatus.RUN); | |
122 | ||
123 | fKernel.put(host, cpu, worker); | |
124 | } | |
125 | return worker; | |
126 | } | |
127 | ||
128 | @Override | |
129 | public void handleEvent(ITmfEvent ev) { | |
130 | String eventName = ev.getName(); | |
5aa7fe19 | 131 | IKernelAnalysisEventLayout eventLayout = getProvider().getEventLayout(ev.getTrace()); |
af7f72ce FG |
132 | |
133 | if (eventName.equals(eventLayout.eventSchedSwitch())) { | |
134 | handleSchedSwitch(ev); | |
af7f72ce FG |
135 | } else if (eventName.equals(eventLayout.eventSoftIrqEntry())) { |
136 | handleSoftirqEntry(ev); | |
137 | } else if (eventName.equals(TcpEventStrings.INET_SOCK_LOCAL_IN) || | |
138 | eventName.equals(TcpEventStrings.NETIF_RECEIVE_SKB)) { | |
139 | handleInetSockLocalIn(ev); | |
140 | } else if (eventName.equals(TcpEventStrings.INET_SOCK_LOCAL_OUT) || | |
141 | eventName.equals(TcpEventStrings.NET_DEV_QUEUE)) { | |
142 | handleInetSockLocalOut(ev); | |
5aa7fe19 FG |
143 | } else if (isWakeupEvent(ev)) { |
144 | handleSchedWakeup(ev); | |
af7f72ce FG |
145 | } |
146 | } | |
147 | ||
148 | private TmfVertex stateExtend(LttngWorker task, long ts) { | |
149 | TmfGraph graph = NonNullUtils.checkNotNull(getProvider().getAssignedGraph()); | |
150 | TmfVertex node = new TmfVertex(ts); | |
151 | ProcessStatus status = task.getStatus(); | |
152 | graph.append(task, node, resolveProcessStatus(status)); | |
153 | return node; | |
154 | } | |
155 | ||
156 | private TmfVertex stateChange(LttngWorker task, long ts) { | |
157 | TmfGraph graph = NonNullUtils.checkNotNull(getProvider().getAssignedGraph()); | |
158 | TmfVertex node = new TmfVertex(ts); | |
159 | ProcessStatus status = task.getOldStatus(); | |
160 | graph.append(task, node, resolveProcessStatus(status)); | |
161 | return node; | |
162 | } | |
163 | ||
164 | private static EdgeType resolveProcessStatus(ProcessStatus status) { | |
165 | EdgeType ret = EdgeType.UNKNOWN; | |
166 | switch (status) { | |
167 | case DEAD: | |
168 | break; | |
169 | case EXIT: | |
170 | case RUN: | |
171 | ret = EdgeType.RUNNING; | |
172 | break; | |
173 | case UNKNOWN: | |
174 | ret = EdgeType.UNKNOWN; | |
175 | break; | |
176 | case WAIT_BLOCKED: | |
177 | ret = EdgeType.BLOCKED; | |
178 | break; | |
179 | case WAIT_CPU: | |
180 | case WAIT_FORK: | |
181 | ret = EdgeType.PREEMPTED; | |
182 | break; | |
183 | case ZOMBIE: | |
184 | ret = EdgeType.UNKNOWN; | |
185 | break; | |
186 | default: | |
187 | break; | |
188 | } | |
189 | return ret; | |
190 | } | |
191 | ||
192 | private void handleSchedSwitch(ITmfEvent event) { | |
193 | String host = event.getTrace().getHostId(); | |
194 | long ts = event.getTimestamp().getValue(); | |
5aa7fe19 | 195 | IKernelAnalysisEventLayout eventLayout = getProvider().getEventLayout(event.getTrace()); |
af7f72ce FG |
196 | LttngSystemModel system = getProvider().getSystem(); |
197 | ||
198 | Integer next = EventField.getInt(event, eventLayout.fieldNextTid()); | |
199 | Integer prev = EventField.getInt(event, eventLayout.fieldPrevTid()); | |
200 | ||
201 | LttngWorker nextTask = system.findWorker(new HostThread(host, next)); | |
202 | LttngWorker prevTask = system.findWorker(new HostThread(host, prev)); | |
203 | ||
204 | if (prevTask == null || nextTask == null) { | |
205 | return; | |
206 | } | |
207 | stateChange(prevTask, ts); | |
208 | stateChange(nextTask, ts); | |
209 | } | |
210 | ||
211 | private void handleSchedWakeup(ITmfEvent event) { | |
212 | TmfGraph graph = NonNullUtils.checkNotNull(getProvider().getAssignedGraph()); | |
213 | String host = event.getTrace().getHostId(); | |
b3867ecc | 214 | Integer cpu = NonNullUtils.checkNotNull(TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event)); |
5aa7fe19 | 215 | IKernelAnalysisEventLayout eventLayout = getProvider().getEventLayout(event.getTrace()); |
af7f72ce FG |
216 | LttngSystemModel system = getProvider().getSystem(); |
217 | ||
218 | long ts = event.getTimestamp().getValue(); | |
219 | Integer tid = EventField.getInt(event, eventLayout.fieldTid()); | |
220 | ||
221 | LttngWorker target = system.findWorker(new HostThread(host, tid)); | |
222 | LttngWorker current = system.getWorkerOnCpu(host, cpu); | |
223 | if (target == null) { | |
224 | return; | |
225 | } | |
226 | ||
227 | ProcessStatus status = target.getOldStatus(); | |
228 | switch (status) { | |
229 | case WAIT_FORK: | |
230 | waitFork(graph, ts, target, current); | |
231 | break; | |
232 | case WAIT_BLOCKED: | |
233 | waitBlocked(event, graph, host, cpu, eventLayout, system, ts, target, current); | |
234 | break; | |
235 | case DEAD: | |
236 | case EXIT: | |
237 | case RUN: | |
238 | case UNKNOWN: | |
239 | case WAIT_CPU: | |
240 | case ZOMBIE: | |
241 | break; | |
242 | default: | |
243 | break; | |
244 | } | |
245 | } | |
246 | ||
5aa7fe19 | 247 | private void waitBlocked(ITmfEvent event, TmfGraph graph, String host, Integer cpu, IKernelAnalysisEventLayout eventLayout, LttngSystemModel system, long ts, LttngWorker target, @Nullable LttngWorker current) { |
af7f72ce FG |
248 | LttngInterruptContext context = system.peekContextStack(host, cpu); |
249 | switch (context.getContext()) { | |
250 | case HRTIMER: | |
251 | // shortcut of appendTaskNode: resolve blocking source in situ | |
252 | graph.append(target, new TmfVertex(ts), EdgeType.TIMER); | |
253 | break; | |
254 | case IRQ: | |
255 | irq(graph, eventLayout, ts, target, context); | |
256 | break; | |
257 | case SOFTIRQ: | |
258 | softIrq(event, graph, cpu, eventLayout, ts, target, context); | |
259 | break; | |
5aa7fe19 FG |
260 | case IPI: |
261 | graph.append(target, new TmfVertex(ts), EdgeType.IPI); | |
262 | break; | |
af7f72ce FG |
263 | case NONE: |
264 | none(ts, target, current); | |
265 | break; | |
266 | default: | |
267 | break; | |
268 | } | |
269 | } | |
270 | ||
5aa7fe19 | 271 | private void softIrq(ITmfEvent event, TmfGraph graph, Integer cpu, IKernelAnalysisEventLayout eventLayout, long ts, LttngWorker target, LttngInterruptContext context) { |
af7f72ce FG |
272 | TmfVertex wup = new TmfVertex(ts); |
273 | TmfEdge l2 = graph.append(target, wup); | |
274 | if (l2 != null) { | |
275 | int vec = EventField.getLong(context.getEvent(), eventLayout.fieldVec()).intValue(); | |
276 | l2.setType(resolveSoftirq(vec)); | |
277 | } | |
278 | // special case for network related softirq | |
279 | Long vec = EventField.getLong(context.getEvent(), eventLayout.fieldVec()); | |
280 | if (vec == LinuxValues.SOFTIRQ_NET_RX || vec == LinuxValues.SOFTIRQ_NET_TX) { | |
281 | // create edge if wake up is caused by incoming packet | |
282 | LttngWorker k = getOrCreateKernelWorker(event, cpu); | |
283 | TmfVertex tail = graph.getTail(k); | |
284 | if (tail != null && tail.getEdge(EdgeDirection.INCOMING_VERTICAL_EDGE) != null) { | |
285 | TmfVertex kwup = stateExtend(k, event.getTimestamp().getValue()); | |
286 | kwup.linkVertical(wup); | |
287 | } | |
288 | } | |
289 | } | |
290 | ||
291 | private void none(long ts, LttngWorker target, @Nullable LttngWorker current) { | |
292 | // task context wakeup | |
293 | if (current != null) { | |
294 | TmfVertex n0 = stateExtend(current, ts); | |
295 | TmfVertex n1 = stateChange(target, ts); | |
296 | n0.linkVertical(n1); | |
297 | } else { | |
298 | stateChange(target, ts); | |
299 | } | |
300 | } | |
301 | ||
5aa7fe19 | 302 | private static void irq(TmfGraph graph, IKernelAnalysisEventLayout eventLayout, long ts, LttngWorker target, LttngInterruptContext context) { |
af7f72ce FG |
303 | TmfEdge link = graph.append(target, new TmfVertex(ts)); |
304 | if (link != null) { | |
305 | int vec = EventField.getLong(context.getEvent(), eventLayout.fieldIrq()).intValue(); | |
306 | link.setType(resolveIRQ(vec)); | |
307 | } | |
308 | } | |
309 | ||
310 | private void waitFork(TmfGraph graph, long ts, LttngWorker target, @Nullable LttngWorker current) { | |
311 | if (current != null) { | |
312 | TmfVertex n0 = stateExtend(current, ts); | |
313 | TmfVertex n1 = stateChange(target, ts); | |
314 | graph.link(n0, n1); | |
315 | } else { | |
316 | stateChange(target, ts); | |
317 | } | |
318 | } | |
319 | ||
320 | private static EdgeType resolveIRQ(int vec) { | |
321 | EdgeType ret = EdgeType.UNKNOWN; | |
322 | switch (vec) { | |
323 | case IRQ_TIMER: | |
324 | ret = EdgeType.INTERRUPTED; | |
325 | break; | |
326 | default: | |
327 | ret = EdgeType.UNKNOWN; | |
328 | break; | |
329 | } | |
330 | return ret; | |
331 | } | |
332 | ||
333 | private static EdgeType resolveSoftirq(int vec) { | |
334 | EdgeType ret = EdgeType.UNKNOWN; | |
335 | switch (vec) { | |
336 | case LinuxValues.SOFTIRQ_HRTIMER: | |
337 | case LinuxValues.SOFTIRQ_TIMER: | |
338 | ret = EdgeType.TIMER; | |
339 | break; | |
340 | case LinuxValues.SOFTIRQ_BLOCK: | |
341 | case LinuxValues.SOFTIRQ_BLOCK_IOPOLL: | |
342 | ret = EdgeType.BLOCK_DEVICE; | |
343 | break; | |
344 | case LinuxValues.SOFTIRQ_NET_RX: | |
345 | case LinuxValues.SOFTIRQ_NET_TX: | |
346 | ret = EdgeType.NETWORK; | |
347 | break; | |
348 | case LinuxValues.SOFTIRQ_SCHED: | |
349 | ret = EdgeType.INTERRUPTED; | |
350 | break; | |
351 | default: | |
352 | ret = EdgeType.UNKNOWN; | |
353 | break; | |
354 | } | |
355 | return ret; | |
356 | } | |
357 | ||
358 | private void handleInetSockLocalIn(ITmfEvent event) { | |
b3867ecc | 359 | Integer cpu = NonNullUtils.checkNotNull(TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event)); |
af7f72ce FG |
360 | String host = event.getTrace().getHostId(); |
361 | LttngSystemModel system = getProvider().getSystem(); | |
362 | ||
363 | LttngInterruptContext intCtx = system.peekContextStack(host, cpu); | |
364 | Context context = intCtx.getContext(); | |
365 | if (context == Context.SOFTIRQ) { | |
366 | LttngWorker k = getOrCreateKernelWorker(event, cpu); | |
367 | TmfVertex endpoint = stateExtend(k, event.getTimestamp().getValue()); | |
368 | fTcpNodes.put(event, endpoint); | |
369 | // TODO add actual progress monitor | |
370 | fTcpMatching.matchEvent(event, event.getTrace(), DEFAULT_PROGRESS_MONITOR); | |
371 | } | |
372 | } | |
373 | ||
374 | private void handleInetSockLocalOut(ITmfEvent event) { | |
b3867ecc | 375 | Integer cpu = NonNullUtils.checkNotNull(TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event)); |
af7f72ce FG |
376 | String host = event.getTrace().getHostId(); |
377 | LttngSystemModel system = getProvider().getSystem(); | |
378 | ||
379 | LttngInterruptContext intCtx = system.peekContextStack(host, cpu); | |
380 | Context context = intCtx.getContext(); | |
381 | ||
382 | LttngWorker sender = null; | |
383 | if (context == Context.NONE) { | |
384 | sender = system.getWorkerOnCpu(event.getTrace().getHostId(), cpu); | |
385 | } else if (context == Context.SOFTIRQ) { | |
386 | sender = getOrCreateKernelWorker(event, cpu); | |
387 | } | |
388 | if (sender == null) { | |
389 | return; | |
390 | } | |
391 | TmfVertex endpoint = stateExtend(sender, event.getTimestamp().getValue()); | |
392 | fTcpNodes.put(event, endpoint); | |
393 | // TODO, add actual progress monitor | |
394 | fTcpMatching.matchEvent(event, event.getTrace(), new NullProgressMonitor()); | |
395 | } | |
396 | ||
397 | private void handleSoftirqEntry(ITmfEvent event) { | |
5aa7fe19 | 398 | IKernelAnalysisEventLayout eventLayout = getProvider().getEventLayout(event.getTrace()); |
af7f72ce FG |
399 | TmfGraph graph = NonNullUtils.checkNotNull(getProvider().getAssignedGraph()); |
400 | Long vec = EventField.getLong(event, eventLayout.fieldVec()); | |
401 | if (vec == LinuxValues.SOFTIRQ_NET_RX || vec == LinuxValues.SOFTIRQ_NET_TX) { | |
b3867ecc | 402 | Integer cpu = NonNullUtils.checkNotNull(TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event)); |
af7f72ce FG |
403 | LttngWorker k = getOrCreateKernelWorker(event, cpu); |
404 | graph.add(k, new TmfVertex(event.getTimestamp().getValue())); | |
405 | } | |
406 | } | |
407 | ||
408 | } |