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>
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
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.internal
.lttng2
.kernel
.core
.stateprovider
;
15 import java
.util
.HashMap
;
16 import java
.util
.Vector
;
17 import java
.util
.concurrent
.BlockingQueue
;
19 import org
.eclipse
.linuxtools
.internal
.lttng2
.kernel
.core
.Attributes
;
20 import org
.eclipse
.linuxtools
.internal
.lttng2
.kernel
.core
.StateValues
;
21 import org
.eclipse
.linuxtools
.lttng2
.kernel
.core
.trace
.LttngStrings
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.ctfadaptor
.CtfTmfEvent
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.event
.ITmfEventField
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.IStateSystemBuilder
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
32 * This is the reference "state provider" for LTTng 2.0 kernel traces.
37 class CtfKernelHandler
implements Runnable
{
39 private final BlockingQueue
<CtfTmfEvent
> inQueue
;
40 private IStateSystemBuilder ss
;
42 private CtfTmfEvent currentEvent
;
45 * We can keep handles to some Attribute Nodes so these don't need to be
46 * re-found (re-hashed Strings etc.) every new event
48 Vector
<Integer
> currentCPUNodes
;
49 Vector
<Integer
> currentThreadNodes
;
51 /* Event names HashMap. TODO: This can be discarded once we move to Java 7 */
52 private final HashMap
<String
, Integer
> knownEventNames
;
54 /* Common locations in the attribute tree */
55 private int cpusNode
= -1;
56 private int threadsNode
= -1;
57 private int irqsNode
= -1;
58 private int softIrqsNode
= -1;
60 CtfKernelHandler(BlockingQueue
<CtfTmfEvent
> eventsQueue
) {
61 assert (eventsQueue
!= null);
62 this.inQueue
= eventsQueue
;
63 currentCPUNodes
= new Vector
<Integer
>();
64 currentThreadNodes
= new Vector
<Integer
>();
66 knownEventNames
= fillEventNames();
69 void assignStateSystem(IStateSystemBuilder targetSS
) {
76 System
.err
.println("Cannot run event manager without assigning a target state system first!"); //$NON-NLS-1$
80 setupCommonLocations();
83 event
= inQueue
.take();
84 while (event
.getTimestampValue() != -1) {
86 event
= inQueue
.take();
88 /* We've received the last event, clean up */
91 } catch (InterruptedException e
) {
92 /* We've been interrupted abnormally */
93 System
.out
.println("Event handler interrupted!"); //$NON-NLS-1$
98 private void closeStateSystem() {
99 /* Close the History system, if there is one */
100 if (currentEvent
== null) {
104 ss
.closeHistory(currentEvent
.getTimestamp().getValue());
105 } catch (TimeRangeException e
) {
107 * Since we're using currentEvent.getTimestamp, this shouldn't
114 private void processEvent(CtfTmfEvent event
) {
115 currentEvent
= event
;
116 ITmfEventField content
= event
.getContent();
117 String eventName
= event
.getEventName();
119 long ts
= event
.getTimestamp().getValue();
121 ITmfStateValue value
;
122 Integer eventCpu
= event
.getCPU();
123 Integer currentCPUNode
, currentThreadNode
, tidNode
;
125 /* Adjust the current nodes Vectors if we see a new CPU in an event */
126 if (eventCpu
>= currentCPUNodes
.size()) {
127 /* We need to add this node to the vector */
128 for (Integer i
= currentCPUNodes
.size(); i
< eventCpu
+ 1; i
++) {
129 quark
= ss
.getQuarkRelativeAndAdd(cpusNode
, i
.toString());
130 currentCPUNodes
.add(quark
);
132 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, Attributes
.UNKNOWN
);
133 currentThreadNodes
.add(quark
);
137 currentCPUNode
= currentCPUNodes
.get(eventCpu
);
138 currentThreadNode
= currentThreadNodes
.get(eventCpu
);
139 assert (currentCPUNode
!= null);
140 assert (currentThreadNode
!= null);
144 * Feed event to the history system if it's known to cause a state
147 switch (getEventIndex(eventName
)) {
149 case 1: // "exit_syscall":
150 /* Fields: int64 ret */
152 /* Clear the current system call on the process */
153 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
154 value
= TmfStateValue
.nullValue();
155 ss
.modifyAttribute(ts
, value
, quark
);
157 /* Put the process' status back to user mode */
158 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
159 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_USERMODE
);
160 ss
.modifyAttribute(ts
, value
, quark
);
164 case 2: // "irq_handler_entry":
165 /* Fields: int32 irq, string name */
167 Integer irqId
= ((Long
) content
.getField(LttngStrings
.IRQ
).getValue()).intValue();
169 /* Mark this IRQ as active in the resource tree.
170 * The state value = the CPU on which this IRQ is sitting */
171 quark
= ss
.getQuarkRelativeAndAdd(irqsNode
, irqId
.toString());
172 value
= TmfStateValue
.newValueInt(event
.getCPU());
173 ss
.modifyAttribute(ts
, value
, quark
);
175 /* Change the status of the running process to interrupted */
176 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
177 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_INTERRUPTED
);
178 ss
.modifyAttribute(ts
, value
, quark
);
180 /* Change the status of the CPU to interrupted */
181 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
182 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_IRQ
);
183 ss
.modifyAttribute(ts
, value
, quark
);
187 case 3: // "irq_handler_exit":
188 /* Fields: int32 irq, int32 ret */
190 Integer irqId
= ((Long
) content
.getField(LttngStrings
.IRQ
).getValue()).intValue();
192 /* Put this IRQ back to inactive in the resource tree */
193 quark
= ss
.getQuarkRelativeAndAdd(irqsNode
, irqId
.toString());
194 value
= TmfStateValue
.nullValue();
195 ss
.modifyAttribute(ts
, value
, quark
);
197 /* Set the previous process back to running */
198 setProcessToRunning(ts
, currentThreadNode
);
200 /* Set the CPU status back to running or "idle" */
201 cpuExitInterrupt(ts
, currentCPUNode
, currentThreadNode
);
205 case 4: // "softirq_entry":
206 /* Fields: int32 vec */
208 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
210 /* Mark this SoftIRQ as active in the resource tree.
211 * The state value = the CPU on which this SoftIRQ is processed */
212 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
213 value
= TmfStateValue
.newValueInt(event
.getCPU());
214 ss
.modifyAttribute(ts
, value
, quark
);
216 /* Change the status of the running process to interrupted */
217 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
218 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_INTERRUPTED
);
219 ss
.modifyAttribute(ts
, value
, quark
);
221 /* Change the status of the CPU to interrupted */
222 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
223 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_SOFTIRQ
);
224 ss
.modifyAttribute(ts
, value
, quark
);
228 case 5: // "softirq_exit":
229 /* Fields: int32 vec */
231 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
233 /* Put this SoftIRQ back to inactive (= -1) in the resource tree */
234 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
235 value
= TmfStateValue
.nullValue();
236 ss
.modifyAttribute(ts
, value
, quark
);
238 /* Set the previous process back to running */
239 setProcessToRunning(ts
, currentThreadNode
);
241 /* Set the CPU status back to "busy" or "idle" */
242 cpuExitInterrupt(ts
, currentCPUNode
, currentThreadNode
);
246 case 6: // "softirq_raise":
247 /* Fields: int32 vec */
249 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
251 /* Mark this SoftIRQ as *raised* in the resource tree.
252 * State value = -2 */
253 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
254 value
= TmfStateValue
.newValueInt(StateValues
.SOFT_IRQ_RAISED
);
255 ss
.modifyAttribute(ts
, value
, quark
);
259 case 7: // "sched_switch":
261 * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 prev_state,
262 * string next_comm, int32 next_tid, int32 next_prio
265 Integer prevTid
= ((Long
) content
.getField(LttngStrings
.PREV_TID
).getValue()).intValue();
266 //Long prevState = (Long) content.getField(LttngStrings.PREV_STATE).getValue();
268 String nextProcessName
= (String
) content
.getField(LttngStrings
.NEXT_COMM
).getValue();
269 Integer nextTid
= ((Long
) content
.getField(LttngStrings
.NEXT_TID
).getValue()).intValue();
271 /* Update the currentThreadNodes pointer */
272 Integer newCurrentThreadNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, nextTid
.toString());
273 currentThreadNodes
.set(eventCpu
, newCurrentThreadNode
);
276 * Set the status of the process that got scheduled out, but
277 * only in the case where that process is currently active.
279 Integer formerThreadNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, prevTid
.toString());
280 quark
= ss
.getQuarkRelativeAndAdd(formerThreadNode
, Attributes
.EXEC_NAME
);
281 value
= ss
.queryOngoingState(quark
);
282 if (!value
.isNull()) {
283 quark
= ss
.getQuarkRelativeAndAdd(formerThreadNode
, Attributes
.STATUS
);
284 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_WAIT
);
285 ss
.modifyAttribute(ts
, value
, quark
);
288 /* Set the status of the new scheduled process */
289 setProcessToRunning(ts
, newCurrentThreadNode
);
291 /* Set the exec name of the new process */
292 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.EXEC_NAME
);
293 value
= TmfStateValue
.newValueString(nextProcessName
);
294 ss
.modifyAttribute(ts
, value
, quark
);
297 * Check if we need to set the syscall state and the PPID of
298 * the new process (in case we haven't seen this process before)
300 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.SYSTEM_CALL
);
301 if (quark
== ss
.getNbAttributes()) { /* Did we just add this attribute? */
302 value
= TmfStateValue
.nullValue();
303 ss
.modifyAttribute(ts
, value
, quark
);
305 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.PPID
);
306 if (quark
== ss
.getNbAttributes()) {
307 value
= TmfStateValue
.nullValue();
308 ss
.modifyAttribute(ts
, value
, quark
);
311 /* Set the current scheduled process on the relevant CPU */
312 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.CURRENT_THREAD
);
313 value
= TmfStateValue
.newValueInt(nextTid
);
314 ss
.modifyAttribute(ts
, value
, quark
);
316 /* Set the status of the CPU itself */
318 /* Check if the entering process is in kernel or user mode */
319 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.SYSTEM_CALL
);
320 if (ss
.queryOngoingState(quark
).isNull()) {
321 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_USERMODE
);
323 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_SYSCALL
);
326 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_IDLE
);
328 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
329 ss
.modifyAttribute(ts
, value
, quark
);
333 case 8: // "sched_process_fork":
334 /* Fields: string parent_comm, int32 parent_tid,
335 * string child_comm, int32 child_tid */
337 // String parentProcessName = (String)
338 // event.getFieldValue("parent_comm");
339 String childProcessName
;
340 childProcessName
= (String
) content
.getField(LttngStrings
.CHILD_COMM
).getValue();
341 // assert ( parentProcessName.equals(childProcessName) );
343 Integer parentTid
= ((Long
) content
.getField(LttngStrings
.PARENT_TID
).getValue()).intValue();
344 Integer childTid
= ((Long
) content
.getField(LttngStrings
.CHILD_TID
).getValue()).intValue();
346 tidNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, childTid
.toString());
348 /* Assign the PPID to the new process */
349 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.PPID
);
350 value
= TmfStateValue
.newValueInt(parentTid
);
351 ss
.modifyAttribute(ts
, value
, quark
);
353 /* Set the new process' exec_name */
354 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.EXEC_NAME
);
355 value
= TmfStateValue
.newValueString(childProcessName
);
356 ss
.modifyAttribute(ts
, value
, quark
);
358 /* Set the new process' status */
359 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.STATUS
);
360 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_WAIT
);
361 ss
.modifyAttribute(ts
, value
, quark
);
363 /* Set the process' syscall state */
364 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.SYSTEM_CALL
);
365 value
= TmfStateValue
.nullValue();
366 ss
.modifyAttribute(ts
, value
, quark
);
370 case 9: // "sched_process_exit":
371 /* Fields: string comm, int32 tid, int32 prio */
373 String processName
= (String
) content
.getField(LttngStrings
.COMM
).getValue();
374 Integer tid
= ((Long
) content
.getField(LttngStrings
.TID
).getValue()).intValue();
376 /* Update the process' name, if we don't have it */
377 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, tid
.toString(), Attributes
.EXEC_NAME
);
378 value
= TmfStateValue
.newValueString(processName
);
379 ss
.updateOngoingState(value
, quark
);
382 * Remove the process and all its sub-attributes from the
385 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, tid
.toString());
386 ss
.removeAttribute(ts
, quark
);
390 case 10: // "sched_process_free":
391 /* Fields: string comm, int32 tid, int32 prio */
394 // FIXME In CTF it's as "syscall_exec". Will have to be adapted.
395 // case LTT_EVENT_EXEC:
396 // filename = new String((byte[]) event.getField(0));
398 // /* Change the Exec_name of the process */
399 // quark = ss.getQuarkRelativePath(true, currentThreadNode,
401 // ss.modifyAttribute(ts, filename, quark);
405 /* Other event types not covered by the main switch */
407 if (eventName
.startsWith(LttngStrings
.SYSCALL_PREFIX
)
408 || eventName
.startsWith(LttngStrings
.COMPAT_SYSCALL_PREFIX
)) {
410 * This is a replacement for the old sys_enter event. Now
411 * syscall names are listed into the event type
414 /* Assign the new system call to the process */
415 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
416 value
= TmfStateValue
.newValueString(eventName
);
417 ss
.modifyAttribute(ts
, value
, quark
);
419 /* Put the process in system call mode */
420 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
421 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_SYSCALL
);
422 ss
.modifyAttribute(ts
, value
, quark
);
426 } // End of big switch
432 /* Number of events of each type, globally */
433 // quark = ss.getQuarkAbsoluteAndAdd(Attributes.STATISTICS,
434 // Attributes.EVENT_TYPES, eventName);
435 // ss.incrementAttribute(ts, quark);
437 /* Number of events per CPU */
438 // quark = ss.getQuarkRelativeAndAdd(currentCPUNode,
439 // Attributes.STATISTICS, Attributes.EVENT_TYPES, eventName);
440 // ss.incrementAttribute(ts, quark);
442 /* Number of events per process */
443 // quark = ss.getQuarkRelativeAndAdd(currentThreadNode,
444 // Attributes.STATISTICS, Attributes.EVENT_TYPES, eventName);
445 // ss.incrementAttribute(ts, quark);
447 } catch (AttributeNotFoundException ae
) {
449 * This would indicate a problem with the logic of the manager here,
450 * so it shouldn't happen.
452 ae
.printStackTrace();
454 } catch (TimeRangeException tre
) {
456 * This would happen if the events in the trace aren't ordered
457 * chronologically, which should never be the case ...
459 System
.err
.println("TimeRangeExcpetion caught in the state system's event manager."); //$NON-NLS-1$
460 System
.err
.println("Are the events in the trace correctly ordered?"); //$NON-NLS-1$
461 tre
.printStackTrace();
463 } catch (StateValueTypeException sve
) {
465 * This would happen if we were trying to push/pop attributes not of
466 * type integer. Which, once again, should never happen.
468 sve
.printStackTrace();
472 private void setupCommonLocations() {
473 cpusNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.CPUS
);
474 threadsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.THREADS
);
475 irqsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.RESOURCES
, Attributes
.IRQS
);
476 softIrqsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.RESOURCES
, Attributes
.SOFT_IRQS
);
479 private static HashMap
<String
, Integer
> fillEventNames() {
481 * TODO Replace with straight strings in the switch/case once we move to
484 HashMap
<String
, Integer
> map
= new HashMap
<String
, Integer
>();
486 map
.put(LttngStrings
.EXIT_SYSCALL
, 1);
487 map
.put(LttngStrings
.IRQ_HANDLER_ENTRY
, 2);
488 map
.put(LttngStrings
.IRQ_HANDLER_EXIT
, 3);
489 map
.put(LttngStrings
.SOFTIRQ_ENTRY
, 4);
490 map
.put(LttngStrings
.SOFTIRQ_EXIT
, 5);
491 map
.put(LttngStrings
.SOFTIRQ_RAISE
, 6);
492 map
.put(LttngStrings
.SCHED_SWITCH
, 7);
493 map
.put(LttngStrings
.SCHED_PROCESS_FORK
, 8);
494 map
.put(LttngStrings
.SCHED_PROCESS_EXIT
, 9);
495 map
.put(LttngStrings
.SCHED_PROCESS_FREE
, 10);
500 private int getEventIndex(String eventName
) {
501 Integer ret
= knownEventNames
.get(eventName
);
502 return (ret
!= null) ? ret
: -1;
506 * When we want to set a process back to a "running" state, first check
507 * its current System_call attribute. If there is a system call active, we
508 * put the process back in the syscall state. If not, we put it back in
511 private void setProcessToRunning(long ts
, int currentThreadNode
)
512 throws AttributeNotFoundException
, TimeRangeException
,
513 StateValueTypeException
{
515 ITmfStateValue value
;
517 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
518 if (ss
.queryOngoingState(quark
).isNull()) {
519 /* We were in user mode before the interruption */
520 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_USERMODE
);
522 /* We were previously in kernel mode */
523 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_SYSCALL
);
525 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
526 ss
.modifyAttribute(ts
, value
, quark
);
530 * Similar logic as above, but to set the CPU's status when it's coming out
531 * of an interruption.
532 * @throws AttributeNotFoundException
533 * @throws StateValueTypeException
534 * @throws TimeRangeException
536 private void cpuExitInterrupt(long ts
, int currentCpuNode
, int currentThreadNode
)
537 throws StateValueTypeException
, AttributeNotFoundException
,
540 ITmfStateValue value
;
542 quark
= ss
.getQuarkRelativeAndAdd(currentCpuNode
, Attributes
.CURRENT_THREAD
);
543 if (ss
.queryOngoingState(quark
).unboxInt() > 0) {
544 /* There was a process on the CPU */
545 quark
= ss
.getQuarkRelative(currentThreadNode
, Attributes
.SYSTEM_CALL
);
546 if (ss
.queryOngoingState(quark
).isNull()) {
547 /* That process was in user mode */
548 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_USERMODE
);
550 /* That process was in a system call */
551 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_SYSCALL
);
554 /* There was no real process scheduled, CPU was idle */
555 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_IDLE
);
557 quark
= ss
.getQuarkRelativeAndAdd(currentCpuNode
, Attributes
.STATUS
);
558 ss
.modifyAttribute(ts
, value
, quark
);