Commit | Line | Data |
---|---|---|
efc403bb AM |
1 | /******************************************************************************* |
2 | * Copyright (c) 2012 Ericsson | |
3 | * Copyright (c) 2010, 2011 École Polytechnique de Montréal | |
4 | * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com> | |
5 | * | |
6 | * All rights reserved. This program and the accompanying materials are | |
7 | * made available under the terms of the Eclipse Public License v1.0 which | |
8 | * accompanies this distribution, and is available at | |
9 | * http://www.eclipse.org/legal/epl-v10.html | |
10 | * | |
11 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.linuxtools.internal.lttng2.kernel.core.stateprovider; | |
14 | ||
15 | import java.util.HashMap; | |
16 | import java.util.Vector; | |
17 | import java.util.concurrent.BlockingQueue; | |
18 | ||
19 | import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfEvent; | |
20 | import org.eclipse.linuxtools.tmf.core.event.ITmfEventField; | |
21 | import org.eclipse.linuxtools.tmf.core.statesystem.AttributeNotFoundException; | |
22 | import org.eclipse.linuxtools.tmf.core.statesystem.StateHistorySystem; | |
23 | import org.eclipse.linuxtools.tmf.core.statesystem.StateSystem; | |
24 | import org.eclipse.linuxtools.tmf.core.statesystem.TimeRangeException; | |
25 | import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue; | |
26 | import org.eclipse.linuxtools.tmf.core.statevalue.StateValueTypeException; | |
27 | import org.eclipse.linuxtools.tmf.core.statevalue.TmfStateValue; | |
28 | ||
29 | /** | |
30 | * This is the reference "state provider" for LTTng 2.0 kernel traces. | |
31 | * | |
32 | * @author alexmont | |
33 | * | |
34 | */ | |
35 | class CTFKernelHandler implements Runnable { | |
36 | ||
37 | private final BlockingQueue<CtfTmfEvent> inQueue; | |
38 | private StateSystem ss; | |
39 | ||
40 | private CtfTmfEvent currentEvent; | |
41 | ||
42 | /* | |
43 | * We can keep handles to some Attribute Nodes so these don't need to be | |
44 | * re-found (re-hashed Strings etc.) every new event | |
45 | */ | |
46 | Vector<Integer> currentCPUNodes; | |
47 | Vector<Integer> currentThreadNodes; | |
48 | ||
49 | /* Event names HashMap. TODO: This can be discarded once we move to Java 7 */ | |
50 | private final HashMap<String, Integer> knownEventNames; | |
51 | ||
52 | CTFKernelHandler(BlockingQueue<CtfTmfEvent> eventsQueue) { | |
53 | assert (eventsQueue != null); | |
54 | this.inQueue = eventsQueue; | |
55 | currentCPUNodes = new Vector<Integer>(); | |
56 | currentThreadNodes = new Vector<Integer>(); | |
57 | ||
58 | knownEventNames = fillEventNames(); | |
59 | } | |
60 | ||
61 | void assignStateSystem(StateSystem targetSS) { | |
62 | this.ss = targetSS; | |
63 | } | |
64 | ||
65 | StateSystem getStateSystem() { | |
66 | return ss; | |
67 | } | |
68 | ||
69 | @Override | |
70 | public void run() { | |
71 | if (ss == null) { | |
72 | System.err.println("Cannot run event manager without assigning a target state system first!"); //$NON-NLS-1$ | |
73 | return; | |
74 | } | |
75 | CtfTmfEvent event; | |
76 | ||
77 | try { | |
78 | event = inQueue.take(); | |
f13dfe18 | 79 | while (event.getTimestampValue() != -1) { |
efc403bb AM |
80 | processEvent(event); |
81 | event = inQueue.take(); | |
82 | } | |
83 | /* We've received the last event, clean up */ | |
84 | closeStateSystem(); | |
85 | return; | |
86 | } catch (InterruptedException e) { | |
87 | /* We've been interrupted abnormally */ | |
88 | System.out.println("Event handler interrupted!"); //$NON-NLS-1$ | |
89 | e.printStackTrace(); | |
90 | } | |
91 | } | |
92 | ||
93 | private void closeStateSystem() { | |
94 | /* Close the History system, if there is one */ | |
95 | if (ss.getClass() == StateHistorySystem.class) { | |
96 | try { | |
97 | ((StateHistorySystem) ss).closeHistory(currentEvent.getTimestamp().getValue()); | |
98 | } catch (TimeRangeException e) { | |
99 | /* | |
100 | * Since we're using currentEvent.getTimestamp, this shouldn't | |
101 | * cause any problem | |
102 | */ | |
103 | e.printStackTrace(); | |
104 | } | |
105 | } | |
106 | } | |
107 | ||
108 | @SuppressWarnings("nls") | |
109 | private void processEvent(CtfTmfEvent event) { | |
110 | currentEvent = event; | |
111 | ITmfEventField content = event.getContent(); | |
2130a4fb | 112 | String eventName = event.getEventName(); |
efc403bb AM |
113 | |
114 | long ts = event.getTimestamp().getValue(); | |
115 | int quark; | |
116 | ITmfStateValue value; | |
117 | Integer eventCpu = event.getCPU(); | |
118 | Integer currentCPUNode, currentThreadNode, tidNode; | |
119 | ||
120 | /* Adjust the current nodes Vectors if we see a new CPU in an event */ | |
121 | if (eventCpu >= currentCPUNodes.size()) { | |
122 | /* We need to add this node to the vector */ | |
123 | for (Integer i = currentCPUNodes.size(); i < eventCpu + 1; i++) { | |
124 | quark = ss.getQuarkAbsoluteAndAdd("CPUs", i.toString()); | |
125 | currentCPUNodes.add(quark); | |
126 | ||
127 | quark = ss.getQuarkAbsoluteAndAdd("Threads", "unknown"); | |
128 | currentThreadNodes.add(quark); | |
129 | } | |
130 | } | |
131 | ||
132 | currentCPUNode = currentCPUNodes.get(eventCpu); | |
133 | currentThreadNode = currentThreadNodes.get(eventCpu); | |
134 | assert (currentCPUNode != null); | |
135 | assert (currentThreadNode != null); | |
136 | ||
137 | try { | |
138 | /* | |
139 | * Feed event to the history system if it's known to cause a state | |
140 | * transition See: | |
141 | * https://projectwiki.dorsal.polymtl.ca/index.php/State_transitions | |
142 | */ | |
143 | switch (getEventIndex(eventName)) { | |
144 | ||
145 | case 1: // "exit_syscall": | |
146 | /* Fields: int64 ret */ | |
147 | /* Pop "syscall" from the Exec_mode_stack */ | |
148 | quark = ss.getQuarkRelativeAndAdd(currentThreadNode, | |
149 | "Exec_mode_stack"); | |
150 | try { | |
151 | ss.popAttribute(ts, quark); | |
152 | } catch (AttributeNotFoundException e1) { | |
153 | /* | |
154 | * meh, can happen if we're missing events, we'll just | |
155 | * silently ignore it. | |
156 | */ | |
157 | System.err.println(event.getTimestamp() | |
158 | + " Popping empty attribute: " + e1.getMessage()); //$NON-NLS-1$ | |
159 | } | |
160 | break; | |
161 | ||
162 | case 2: // "irq_handler_entry": | |
163 | /* Fields: int32 irq, string name */ | |
164 | Integer irqId = ((Long) content.getField("irq").getValue()).intValue(); | |
165 | ||
166 | /* Push the IRQ to the CPU's IRQ_stack */ | |
167 | quark = ss.getQuarkRelativeAndAdd(currentCPUNode, "IRQ_stack"); | |
168 | value = TmfStateValue.newValueInt(irqId); | |
169 | ss.pushAttribute(ts, value, quark); | |
170 | ||
171 | /* Change the status of the running process to interrupted */ | |
172 | quark = ss.getQuarkRelativeAndAdd(currentThreadNode, "Status"); | |
173 | value = TmfStateValue.newValueInt(STATE_PROCESS_STATUS_WAIT_CPU); | |
174 | ss.modifyAttribute(ts, value, quark); | |
175 | break; | |
176 | ||
177 | case 3: // "irq_handler_exit": | |
178 | /* Fields: int32 irq, int32 ret */ | |
179 | int stackDepth = 0; | |
180 | ||
181 | /* Pop the IRQ from the CPU's IRQ_stack */ | |
182 | quark = ss.getQuarkRelativeAndAdd(currentCPUNode, "IRQ_stack"); | |
183 | try { | |
184 | ss.popAttribute(ts, quark); | |
185 | } catch (AttributeNotFoundException e1) { | |
186 | System.err.print(event.getTimestamp() | |
187 | + " Popping empty attribute: " + e1.getMessage()); | |
188 | } | |
189 | ||
190 | /* | |
191 | * If this was the last IRQ on the stack, set the process back | |
192 | * to running | |
193 | */ | |
194 | /* 'quark' should still be valid */ | |
195 | try { | |
196 | stackDepth = ss.queryOngoingState(quark).unboxInt(); | |
197 | } catch (StateValueTypeException e) { | |
198 | /* IRQ_stack SHOULD be of int type, this shouldn't happen */ | |
199 | e.printStackTrace(); | |
200 | } | |
201 | if (stackDepth == 0) { | |
202 | quark = ss.getQuarkRelativeAndAdd(currentThreadNode, | |
203 | "Status"); | |
204 | value = TmfStateValue.newValueInt(STATE_PROCESS_STATUS_RUN); | |
205 | ss.modifyAttribute(ts, value, quark); | |
206 | } | |
207 | break; | |
208 | ||
209 | case 4: // "softirq_entry": | |
210 | /* Fields: int32 vec */ | |
211 | break; | |
212 | ||
213 | case 5: // "softirq_exit": | |
214 | /* Fields: int32 vec */ | |
215 | break; | |
216 | ||
217 | case 6: // "softirq_raise": | |
218 | /* Fields: int32 vec */ | |
219 | break; | |
220 | ||
221 | case 7: // "sched_switch": | |
222 | /* | |
223 | * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, | |
224 | * int64 prev_state, string next_comm, int32 next_tid, int32 | |
225 | * next_prio | |
226 | */ | |
227 | ||
228 | // prev_comm doesn't seem to get populated... | |
229 | String prevProcessName = (String) content.getField("prev_comm").getValue(); | |
230 | Integer prevTid = ((Long) content.getField("prev_tid").getValue()).intValue(); | |
231 | Long prevState = (Long) content.getField("prev_state").getValue(); | |
232 | ||
233 | String nextProcessName = (String) content.getField("next_comm").getValue(); | |
234 | Integer nextTid = ((Long) content.getField("next_tid").getValue()).intValue(); | |
235 | ||
236 | /* Update the name of the process going out (if needed) */ | |
237 | quark = ss.getQuarkRelativeAndAdd(currentThreadNode, | |
238 | "Exec_name"); | |
239 | value = TmfStateValue.newValueString(prevProcessName); | |
240 | ss.updateOngoingState(value, quark); | |
241 | ||
242 | /* Update the currentThreadNodes pointer */ | |
243 | Integer newCurrentThreadNode = ss.getQuarkAbsoluteAndAdd( | |
244 | "Threads", nextTid.toString()); | |
245 | currentThreadNodes.set(eventCpu, newCurrentThreadNode); | |
246 | ||
247 | /* Set the status of the new scheduled process */ | |
248 | quark = ss.getQuarkRelativeAndAdd(newCurrentThreadNode, | |
249 | "Status"); | |
250 | value = TmfStateValue.newValueInt(STATE_PROCESS_STATUS_RUN); | |
251 | ss.modifyAttribute(ts, value, quark); | |
252 | ||
253 | /* Set the exec name of the new process */ | |
254 | quark = ss.getQuarkRelativeAndAdd(newCurrentThreadNode, | |
255 | "Exec_name"); | |
256 | value = TmfStateValue.newValueString(nextProcessName); | |
257 | ss.modifyAttribute(ts, value, quark); | |
258 | ||
259 | /* Set the status of the process that got scheduled out */ | |
260 | quark = ss.getQuarkAbsoluteAndAdd("Threads", | |
261 | prevTid.toString(), "Status"); | |
262 | value = TmfStateValue.newValueInt(prevState.intValue()); | |
263 | ss.modifyAttribute(ts, value, quark); | |
264 | ||
265 | /* Set the current scheduled process on the relevant CPU */ | |
266 | quark = ss.getQuarkRelativeAndAdd(currentCPUNode, | |
267 | "Current_thread"); | |
268 | value = TmfStateValue.newValueInt(nextTid); | |
269 | ss.modifyAttribute(ts, value, quark); | |
270 | break; | |
271 | ||
272 | case 8: // "sched_process_fork": | |
273 | /* | |
274 | * Fields: string parent_comm, int32 parent_tid, string | |
275 | * child_comm, int32 child_tid | |
276 | */ | |
277 | ||
278 | // String parentProcessName = (String) | |
279 | // event.getFieldValue("parent_comm"); | |
634c67e6 AM |
280 | String childProcessName; |
281 | childProcessName = (String) content.getField("child_comm").getValue(); | |
efc403bb AM |
282 | // assert ( parentProcessName.equals(childProcessName) ); |
283 | ||
284 | Integer parentTid = ((Long) content.getField("parent_tid").getValue()).intValue(); | |
285 | Integer childTid = ((Long) content.getField("child_tid").getValue()).intValue(); | |
286 | ||
287 | tidNode = ss.getQuarkAbsoluteAndAdd("Threads", | |
288 | childTid.toString()); | |
289 | ||
290 | /* | |
291 | * Add the new process with its known TID, PPID, and initial | |
292 | * Exec_name | |
293 | */ | |
294 | quark = ss.getQuarkRelativeAndAdd(tidNode, "PPID"); | |
295 | value = TmfStateValue.newValueInt(parentTid); | |
296 | ss.modifyAttribute(ts, value, quark); | |
297 | ||
298 | /* Set the new process' exec_name */ | |
299 | quark = ss.getQuarkRelativeAndAdd(tidNode, "Exec_name"); | |
300 | value = TmfStateValue.newValueString(childProcessName); | |
301 | ss.modifyAttribute(ts, value, quark); | |
302 | break; | |
303 | ||
304 | case 9: // "sched_process_exit": | |
305 | /* Fields: string comm, int32 tid, int32 prio */ | |
306 | String processName = (String) content.getField("comm").getValue(); | |
307 | Integer tid = ((Long) content.getField("tid").getValue()).intValue(); | |
308 | ||
309 | /* Update the process' name, if we don't have it */ | |
310 | quark = ss.getQuarkAbsoluteAndAdd("Threads", tid.toString(), | |
311 | "Exec_name"); | |
312 | value = TmfStateValue.newValueString(processName); | |
313 | ss.updateOngoingState(value, quark); | |
314 | ||
315 | /* | |
316 | * Remove the process and all its sub-attributes from the | |
317 | * current state | |
318 | */ | |
319 | quark = ss.getQuarkAbsoluteAndAdd("Threads", tid.toString()); | |
320 | ss.removeAttribute(ts, quark); | |
321 | break; | |
322 | ||
323 | case 10: // "sched_process_free": | |
324 | /* Fields: string comm, int32 tid, int32 prio */ | |
325 | break; | |
326 | ||
327 | // FIXME Not available with CTF. Use event context? | |
328 | // case LTT_EVENT_EXEC: | |
329 | // filename = new String((byte[]) event.getField(0)); | |
330 | // | |
331 | // /* Change the Exec_name of the process */ | |
332 | // quark = ss.getQuarkRelativePath(true, currentThreadNode, | |
333 | // "Exec_name"); | |
334 | // ss.modifyAttribute(ts, filename, quark); | |
335 | // break; | |
336 | ||
337 | default: | |
338 | /* Other event types not covered by the main switch */ | |
339 | ||
340 | if (eventName.startsWith("sys_") | |
341 | || eventName.startsWith("compat_sys_")) { | |
342 | /* | |
343 | * This is a replacement for the old sys_enter event. Now | |
344 | * syscall names are listed into the event type | |
345 | */ | |
346 | ||
347 | /* | |
348 | * Push the syscall name on the Exec_mode_stack of the | |
349 | * relevant PID | |
350 | */ | |
351 | quark = ss.getQuarkRelativeAndAdd(currentThreadNode, | |
352 | "Exec_mode_stack"); | |
353 | value = TmfStateValue.newValueString(eventName); | |
354 | ss.pushAttribute(ts, value, quark); | |
355 | } | |
356 | ||
357 | break; | |
358 | } // End of switch | |
359 | ||
360 | /* | |
361 | * Statistics | |
362 | */ | |
363 | ||
634c67e6 AM |
364 | /* Number of events of each type, globally */ |
365 | // quark = ss.getQuarkAbsoluteAndAdd("Stats", "Event_types", | |
366 | // eventName); | |
367 | // ss.incrementAttribute(ts, quark); | |
368 | ||
369 | /* Nb of events per CPU */ | |
370 | // quark = ss.getQuarkRelativeAndAdd(currentCPUNode, "Stats", | |
371 | // "Event_types", eventName); | |
372 | // ss.incrementAttribute(ts, quark); | |
373 | ||
374 | /* Nb of events per process */ | |
375 | // quark = ss.getQuarkRelativeAndAdd(currentThreadNode, "Stats", | |
376 | // "Event_types", eventName); | |
377 | // ss.incrementAttribute(ts, quark); | |
efc403bb AM |
378 | |
379 | // end of big non-indented try | |
380 | } catch (AttributeNotFoundException ae) { | |
381 | /* | |
382 | * This would indicate a problem with the logic of the manager here, | |
383 | * so it shouldn't happen. | |
384 | */ | |
385 | ae.printStackTrace(); | |
386 | ||
387 | } catch (TimeRangeException tre) { | |
388 | /* | |
389 | * This would happen if the events in the trace aren't ordered | |
390 | * chronologically, which should never be the case ... | |
391 | */ | |
392 | System.err.println("TimeRangeExcpetion caught in the state system's event manager."); | |
393 | System.err.println("Are the events in the trace correctly ordered?"); | |
394 | tre.printStackTrace(); | |
395 | ||
396 | } catch (StateValueTypeException sve) { | |
397 | /* | |
398 | * This would happen if we were trying to push/pop attributes not of | |
399 | * type integer. Which, once again, should never happen. | |
400 | */ | |
401 | sve.printStackTrace(); | |
402 | } | |
403 | ||
404 | } | |
405 | ||
406 | @SuppressWarnings("nls") | |
407 | private static HashMap<String, Integer> fillEventNames() { | |
408 | /* | |
409 | * TODO Replace with straight strings in the switch/case once we move to | |
410 | * Java 7 | |
411 | */ | |
412 | /* | |
413 | * This is still, imo, cleaner than the wtf-were-they-thinking Java | |
414 | * Enums | |
415 | */ | |
416 | HashMap<String, Integer> map = new HashMap<String, Integer>(); | |
417 | ||
418 | map.put("exit_syscall", 1); | |
419 | map.put("irq_handler_entry", 2); | |
420 | map.put("irq_handler_exit", 3); | |
421 | map.put("softirq_entry", 4); | |
422 | map.put("softirq_exit", 5); | |
423 | map.put("softirq_raise", 6); | |
424 | map.put("sched_switch", 7); | |
425 | map.put("sched_process_fork", 8); | |
426 | map.put("sched_process_exit", 9); | |
427 | map.put("sched_process_free", 10); | |
428 | ||
429 | return map; | |
430 | } | |
431 | ||
432 | private int getEventIndex(String eventName) { | |
433 | Integer ret = knownEventNames.get(eventName); | |
434 | return (ret != null) ? ret : -1; | |
435 | } | |
436 | ||
437 | /* Process status */ | |
438 | private final static int STATE_PROCESS_STATUS_WAIT_CPU = 1; | |
439 | private final static int STATE_PROCESS_STATUS_RUN = 2; | |
440 | } |