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
.ArrayList
;
16 import java
.util
.HashMap
;
17 import java
.util
.List
;
18 import java
.util
.concurrent
.BlockingQueue
;
20 import org
.eclipse
.linuxtools
.internal
.lttng2
.kernel
.core
.Attributes
;
21 import org
.eclipse
.linuxtools
.internal
.lttng2
.kernel
.core
.LttngStrings
;
22 import org
.eclipse
.linuxtools
.internal
.lttng2
.kernel
.core
.StateValues
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.ctfadaptor
.CtfTmfEvent
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.event
.ITmfEventField
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.IStateSystemBuilder
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
33 * This is the reference "state provider" for LTTng 2.0 kernel traces.
38 class CtfKernelHandler
implements Runnable
{
40 private final BlockingQueue
<CtfTmfEvent
> inQueue
;
41 private IStateSystemBuilder ss
;
43 private CtfTmfEvent currentEvent
;
46 * We can keep handles to some Attribute Nodes so these don't need to be
47 * re-found (re-hashed Strings etc.) every new event
49 List
<Integer
> currentCPUNodes
;
50 List
<Integer
> currentThreadNodes
;
52 /* Event names HashMap. TODO: This can be discarded once we move to Java 7 */
53 private final HashMap
<String
, Integer
> knownEventNames
;
55 /* Common locations in the attribute tree */
56 private int cpusNode
= -1;
57 private int threadsNode
= -1;
58 private int irqsNode
= -1;
59 private int softIrqsNode
= -1;
61 CtfKernelHandler(BlockingQueue
<CtfTmfEvent
> eventsQueue
) {
62 assert (eventsQueue
!= null);
63 this.inQueue
= eventsQueue
;
64 currentCPUNodes
= new ArrayList
<Integer
>();
65 currentThreadNodes
= new ArrayList
<Integer
>();
67 knownEventNames
= fillEventNames();
70 void assignStateSystem(IStateSystemBuilder targetSS
) {
77 System
.err
.println("Cannot run event manager without assigning a target state system first!"); //$NON-NLS-1$
81 setupCommonLocations();
84 event
= inQueue
.take();
85 while (event
.getTimestampValue() != -1) {
87 event
= inQueue
.take();
89 /* We've received the last event, clean up */
92 } catch (InterruptedException e
) {
93 /* We've been interrupted abnormally */
94 System
.out
.println("Event handler interrupted!"); //$NON-NLS-1$
99 private void closeStateSystem() {
100 /* Close the History system, if there is one */
101 if (currentEvent
== null) {
105 ss
.closeHistory(currentEvent
.getTimestamp().getValue());
106 } catch (TimeRangeException e
) {
108 * Since we're using currentEvent.getTimestamp, this shouldn't
115 private void processEvent(CtfTmfEvent event
) {
116 currentEvent
= event
;
117 ITmfEventField content
= event
.getContent();
118 String eventName
= event
.getEventName();
120 long ts
= event
.getTimestamp().getValue();
122 ITmfStateValue value
;
123 Integer eventCpu
= event
.getCPU();
124 Integer currentCPUNode
, currentThreadNode
;
126 /* Adjust the current nodes Vectors if we see a new CPU in an event */
127 if (eventCpu
>= currentCPUNodes
.size()) {
128 /* We need to add this node to the vector */
129 for (Integer i
= currentCPUNodes
.size(); i
< eventCpu
+ 1; i
++) {
130 quark
= ss
.getQuarkRelativeAndAdd(cpusNode
, i
.toString());
131 currentCPUNodes
.add(quark
);
133 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, Attributes
.UNKNOWN
);
134 currentThreadNodes
.add(quark
);
138 currentCPUNode
= currentCPUNodes
.get(eventCpu
);
139 currentThreadNode
= currentThreadNodes
.get(eventCpu
);
140 assert (currentCPUNode
!= null);
141 assert (currentThreadNode
!= null);
145 * Feed event to the history system if it's known to cause a state
148 switch (getEventIndex(eventName
)) {
150 case 1: // "exit_syscall":
151 /* Fields: int64 ret */
153 /* Clear the current system call on the process */
154 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
155 value
= TmfStateValue
.nullValue();
156 ss
.modifyAttribute(ts
, value
, quark
);
158 /* Put the process' status back to user mode */
159 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
160 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_USERMODE
);
161 ss
.modifyAttribute(ts
, value
, quark
);
163 /* Put the CPU's status back to user mode */
164 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
165 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_USERMODE
);
166 ss
.modifyAttribute(ts
, value
, quark
);
170 case 2: // "irq_handler_entry":
171 /* Fields: int32 irq, string name */
173 Integer irqId
= ((Long
) content
.getField(LttngStrings
.IRQ
).getValue()).intValue();
175 /* Mark this IRQ as active in the resource tree.
176 * The state value = the CPU on which this IRQ is sitting */
177 quark
= ss
.getQuarkRelativeAndAdd(irqsNode
, irqId
.toString());
178 value
= TmfStateValue
.newValueInt(event
.getCPU());
179 ss
.modifyAttribute(ts
, value
, quark
);
181 /* Change the status of the running process to interrupted */
182 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
183 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_INTERRUPTED
);
184 ss
.modifyAttribute(ts
, value
, quark
);
186 /* Change the status of the CPU to interrupted */
187 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
188 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_IRQ
);
189 ss
.modifyAttribute(ts
, value
, quark
);
193 case 3: // "irq_handler_exit":
194 /* Fields: int32 irq, int32 ret */
196 Integer irqId
= ((Long
) content
.getField(LttngStrings
.IRQ
).getValue()).intValue();
198 /* Put this IRQ back to inactive in the resource tree */
199 quark
= ss
.getQuarkRelativeAndAdd(irqsNode
, irqId
.toString());
200 value
= TmfStateValue
.nullValue();
201 ss
.modifyAttribute(ts
, value
, quark
);
203 /* Set the previous process back to running */
204 setProcessToRunning(ts
, currentThreadNode
);
206 /* Set the CPU status back to running or "idle" */
207 cpuExitInterrupt(ts
, currentCPUNode
, currentThreadNode
);
211 case 4: // "softirq_entry":
212 /* Fields: int32 vec */
214 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
216 /* Mark this SoftIRQ as active in the resource tree.
217 * The state value = the CPU on which this SoftIRQ is processed */
218 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
219 value
= TmfStateValue
.newValueInt(event
.getCPU());
220 ss
.modifyAttribute(ts
, value
, quark
);
222 /* Change the status of the running process to interrupted */
223 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
224 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_INTERRUPTED
);
225 ss
.modifyAttribute(ts
, value
, quark
);
227 /* Change the status of the CPU to interrupted */
228 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
229 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_SOFTIRQ
);
230 ss
.modifyAttribute(ts
, value
, quark
);
234 case 5: // "softirq_exit":
235 /* Fields: int32 vec */
237 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
239 /* Put this SoftIRQ back to inactive (= -1) in the resource tree */
240 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
241 value
= TmfStateValue
.nullValue();
242 ss
.modifyAttribute(ts
, value
, quark
);
244 /* Set the previous process back to running */
245 setProcessToRunning(ts
, currentThreadNode
);
247 /* Set the CPU status back to "busy" or "idle" */
248 cpuExitInterrupt(ts
, currentCPUNode
, currentThreadNode
);
252 case 6: // "softirq_raise":
253 /* Fields: int32 vec */
255 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
257 /* Mark this SoftIRQ as *raised* in the resource tree.
258 * State value = -2 */
259 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
260 value
= TmfStateValue
.newValueInt(StateValues
.SOFT_IRQ_RAISED
);
261 ss
.modifyAttribute(ts
, value
, quark
);
265 case 7: // "sched_switch":
267 * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 prev_state,
268 * string next_comm, int32 next_tid, int32 next_prio
271 Integer prevTid
= ((Long
) content
.getField(LttngStrings
.PREV_TID
).getValue()).intValue();
272 //Long prevState = (Long) content.getField(LttngStrings.PREV_STATE).getValue();
273 String nextProcessName
= (String
) content
.getField(LttngStrings
.NEXT_COMM
).getValue();
274 Integer nextTid
= ((Long
) content
.getField(LttngStrings
.NEXT_TID
).getValue()).intValue();
276 Integer formerThreadNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, prevTid
.toString());
277 Integer newCurrentThreadNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, nextTid
.toString());
279 /* Update the currentThreadNodes pointer */
280 currentThreadNodes
.set(eventCpu
, newCurrentThreadNode
);
282 /* Set the status of the process that got scheduled out. */
283 quark
= ss
.getQuarkRelativeAndAdd(formerThreadNode
, Attributes
.STATUS
);
284 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_WAIT
);
285 ss
.modifyAttribute(ts
, value
, quark
);
287 /* Set the status of the new scheduled process */
288 setProcessToRunning(ts
, newCurrentThreadNode
);
290 /* Set the exec name of the new process */
291 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.EXEC_NAME
);
292 value
= TmfStateValue
.newValueString(nextProcessName
);
293 ss
.modifyAttribute(ts
, value
, quark
);
296 * Check if we need to set the syscall state and the PPID of
297 * the new process (in case we haven't seen this process before)
299 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.SYSTEM_CALL
);
300 if (quark
== ss
.getNbAttributes()) { /* Did we just add this attribute? */
301 value
= TmfStateValue
.nullValue();
302 ss
.modifyAttribute(ts
, value
, quark
);
304 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.PPID
);
305 if (quark
== ss
.getNbAttributes()) {
306 value
= TmfStateValue
.nullValue();
307 ss
.modifyAttribute(ts
, value
, quark
);
310 /* Set the current scheduled process on the relevant CPU */
311 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.CURRENT_THREAD
);
312 value
= TmfStateValue
.newValueInt(nextTid
);
313 ss
.modifyAttribute(ts
, value
, quark
);
315 /* Set the status of the CPU itself */
317 /* Check if the entering process is in kernel or user mode */
318 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.SYSTEM_CALL
);
319 if (ss
.queryOngoingState(quark
).isNull()) {
320 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_USERMODE
);
322 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_SYSCALL
);
325 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_IDLE
);
327 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
328 ss
.modifyAttribute(ts
, value
, quark
);
332 case 8: // "sched_process_fork":
333 /* Fields: string parent_comm, int32 parent_tid,
334 * string child_comm, int32 child_tid */
336 // String parentProcessName = (String) event.getFieldValue("parent_comm");
337 String childProcessName
= (String
) content
.getField(LttngStrings
.CHILD_COMM
).getValue();
338 // assert ( parentProcessName.equals(childProcessName) );
340 Integer parentTid
= ((Long
) content
.getField(LttngStrings
.PARENT_TID
).getValue()).intValue();
341 Integer childTid
= ((Long
) content
.getField(LttngStrings
.CHILD_TID
).getValue()).intValue();
343 Integer parentTidNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, parentTid
.toString());
344 Integer childTidNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, childTid
.toString());
346 /* Assign the PPID to the new process */
347 quark
= ss
.getQuarkRelativeAndAdd(childTidNode
, Attributes
.PPID
);
348 value
= TmfStateValue
.newValueInt(parentTid
);
349 ss
.modifyAttribute(ts
, value
, quark
);
351 /* Set the new process' exec_name */
352 quark
= ss
.getQuarkRelativeAndAdd(childTidNode
, Attributes
.EXEC_NAME
);
353 value
= TmfStateValue
.newValueString(childProcessName
);
354 ss
.modifyAttribute(ts
, value
, quark
);
356 /* Set the new process' status */
357 quark
= ss
.getQuarkRelativeAndAdd(childTidNode
, Attributes
.STATUS
);
358 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_WAIT
);
359 ss
.modifyAttribute(ts
, value
, quark
);
361 /* Set the process' syscall name, to be the same as the parent's */
362 quark
= ss
.getQuarkRelativeAndAdd(parentTidNode
, Attributes
.SYSTEM_CALL
);
363 value
= ss
.queryOngoingState(quark
);
364 quark
= ss
.getQuarkRelativeAndAdd(childTidNode
, Attributes
.SYSTEM_CALL
);
365 ss
.modifyAttribute(ts
, value
, quark
);
369 case 9: // "sched_process_exit":
370 /* Fields: string comm, int32 tid, int32 prio */
373 case 10: // "sched_process_free":
374 /* Fields: string comm, int32 tid, int32 prio */
376 * A sched_process_free will always happen after the sched_switch
377 * that will remove the process from the cpu for the last time. So
378 * this is when we should delete everything wrt to the process.
381 Integer tid
= ((Long
) content
.getField(LttngStrings
.TID
).getValue()).intValue();
383 * Remove the process and all its sub-attributes from the
386 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, tid
.toString());
387 ss
.removeAttribute(ts
, quark
);
391 // FIXME In CTF it's as "syscall_exec". Will have to be adapted.
392 // case LTT_EVENT_EXEC:
393 // filename = new String((byte[]) event.getField(0));
395 // /* Change the Exec_name of the process */
396 // quark = ss.getQuarkRelativePath(true, currentThreadNode,
398 // ss.modifyAttribute(ts, filename, quark);
402 /* Other event types not covered by the main switch */
404 if (eventName
.startsWith(LttngStrings
.SYSCALL_PREFIX
)
405 || eventName
.startsWith(LttngStrings
.COMPAT_SYSCALL_PREFIX
)) {
407 * This is a replacement for the old sys_enter event. Now
408 * syscall names are listed into the event type
411 /* Assign the new system call to the process */
412 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
413 value
= TmfStateValue
.newValueString(eventName
);
414 ss
.modifyAttribute(ts
, value
, quark
);
416 /* Put the process in system call mode */
417 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
418 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_SYSCALL
);
419 ss
.modifyAttribute(ts
, value
, quark
);
421 /* Put the CPU in system call (kernel) mode */
422 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.STATUS
);
423 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_SYSCALL
);
424 ss
.modifyAttribute(ts
, value
, quark
);
428 } // End of big switch
434 /* Number of events of each type, globally */
435 // quark = ss.getQuarkAbsoluteAndAdd(Attributes.STATISTICS,
436 // Attributes.EVENT_TYPES, eventName);
437 // ss.incrementAttribute(ts, quark);
439 /* Number of events per CPU */
440 // quark = ss.getQuarkRelativeAndAdd(currentCPUNode,
441 // Attributes.STATISTICS, Attributes.EVENT_TYPES, eventName);
442 // ss.incrementAttribute(ts, quark);
444 /* Number of events per process */
445 // quark = ss.getQuarkRelativeAndAdd(currentThreadNode,
446 // Attributes.STATISTICS, Attributes.EVENT_TYPES, eventName);
447 // ss.incrementAttribute(ts, quark);
449 } catch (AttributeNotFoundException ae
) {
451 * This would indicate a problem with the logic of the manager here,
452 * so it shouldn't happen.
454 ae
.printStackTrace();
456 } catch (TimeRangeException tre
) {
458 * This would happen if the events in the trace aren't ordered
459 * chronologically, which should never be the case ...
461 System
.err
.println("TimeRangeExcpetion caught in the state system's event manager."); //$NON-NLS-1$
462 System
.err
.println("Are the events in the trace correctly ordered?"); //$NON-NLS-1$
463 tre
.printStackTrace();
465 } catch (StateValueTypeException sve
) {
467 * This would happen if we were trying to push/pop attributes not of
468 * type integer. Which, once again, should never happen.
470 sve
.printStackTrace();
474 private void setupCommonLocations() {
475 cpusNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.CPUS
);
476 threadsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.THREADS
);
477 irqsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.RESOURCES
, Attributes
.IRQS
);
478 softIrqsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.RESOURCES
, Attributes
.SOFT_IRQS
);
481 private static HashMap
<String
, Integer
> fillEventNames() {
483 * TODO Replace with straight strings in the switch/case once we move to
486 HashMap
<String
, Integer
> map
= new HashMap
<String
, Integer
>();
488 map
.put(LttngStrings
.EXIT_SYSCALL
, 1);
489 map
.put(LttngStrings
.IRQ_HANDLER_ENTRY
, 2);
490 map
.put(LttngStrings
.IRQ_HANDLER_EXIT
, 3);
491 map
.put(LttngStrings
.SOFTIRQ_ENTRY
, 4);
492 map
.put(LttngStrings
.SOFTIRQ_EXIT
, 5);
493 map
.put(LttngStrings
.SOFTIRQ_RAISE
, 6);
494 map
.put(LttngStrings
.SCHED_SWITCH
, 7);
495 map
.put(LttngStrings
.SCHED_PROCESS_FORK
, 8);
496 map
.put(LttngStrings
.SCHED_PROCESS_EXIT
, 9);
497 map
.put(LttngStrings
.SCHED_PROCESS_FREE
, 10);
502 private int getEventIndex(String eventName
) {
503 Integer ret
= knownEventNames
.get(eventName
);
504 return (ret
!= null) ? ret
: -1;
508 * When we want to set a process back to a "running" state, first check
509 * its current System_call attribute. If there is a system call active, we
510 * put the process back in the syscall state. If not, we put it back in
513 private void setProcessToRunning(long ts
, int currentThreadNode
)
514 throws AttributeNotFoundException
, TimeRangeException
,
515 StateValueTypeException
{
517 ITmfStateValue value
;
519 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
520 if (ss
.queryOngoingState(quark
).isNull()) {
521 /* We were in user mode before the interruption */
522 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_USERMODE
);
524 /* We were previously in kernel mode */
525 value
= TmfStateValue
.newValueInt(StateValues
.PROCESS_STATUS_RUN_SYSCALL
);
527 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
528 ss
.modifyAttribute(ts
, value
, quark
);
532 * Similar logic as above, but to set the CPU's status when it's coming out
533 * of an interruption.
534 * @throws AttributeNotFoundException
535 * @throws StateValueTypeException
536 * @throws TimeRangeException
538 private void cpuExitInterrupt(long ts
, int currentCpuNode
, int currentThreadNode
)
539 throws StateValueTypeException
, AttributeNotFoundException
,
542 ITmfStateValue value
;
544 quark
= ss
.getQuarkRelativeAndAdd(currentCpuNode
, Attributes
.CURRENT_THREAD
);
545 if (ss
.queryOngoingState(quark
).unboxInt() > 0) {
546 /* There was a process on the CPU */
547 quark
= ss
.getQuarkRelative(currentThreadNode
, Attributes
.SYSTEM_CALL
);
548 if (ss
.queryOngoingState(quark
).isNull()) {
549 /* That process was in user mode */
550 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_USERMODE
);
552 /* That process was in a system call */
553 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_RUN_SYSCALL
);
556 /* There was no real process scheduled, CPU was idle */
557 value
= TmfStateValue
.newValueInt(StateValues
.CPU_STATUS_IDLE
);
559 quark
= ss
.getQuarkRelativeAndAdd(currentCpuNode
, Attributes
.STATUS
);
560 ss
.modifyAttribute(ts
, value
, quark
);