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
.lttng2
.kernel
.core
.trace
.Attributes
;
20 import org
.eclipse
.linuxtools
.lttng2
.kernel
.core
.trace
.LttngStrings
;
21 import org
.eclipse
.linuxtools
.tmf
.core
.ctfadaptor
.CtfTmfEvent
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.event
.ITmfEventField
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.IStateSystemBuilder
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
31 * This is the reference "state provider" for LTTng 2.0 kernel traces.
36 class CtfKernelHandler
implements Runnable
{
38 private final BlockingQueue
<CtfTmfEvent
> inQueue
;
39 private IStateSystemBuilder ss
;
41 private CtfTmfEvent currentEvent
;
44 * We can keep handles to some Attribute Nodes so these don't need to be
45 * re-found (re-hashed Strings etc.) every new event
47 Vector
<Integer
> currentCPUNodes
;
48 Vector
<Integer
> currentThreadNodes
;
50 /* Event names HashMap. TODO: This can be discarded once we move to Java 7 */
51 private final HashMap
<String
, Integer
> knownEventNames
;
53 /* Common locations in the attribute tree */
54 private int cpusNode
= -1;
55 private int threadsNode
= -1;
56 private int irqsNode
= -1;
57 private int softIrqsNode
= -1;
59 CtfKernelHandler(BlockingQueue
<CtfTmfEvent
> eventsQueue
) {
60 assert (eventsQueue
!= null);
61 this.inQueue
= eventsQueue
;
62 currentCPUNodes
= new Vector
<Integer
>();
63 currentThreadNodes
= new Vector
<Integer
>();
65 knownEventNames
= fillEventNames();
68 void assignStateSystem(IStateSystemBuilder targetSS
) {
75 System
.err
.println("Cannot run event manager without assigning a target state system first!"); //$NON-NLS-1$
79 setupCommonLocations();
82 event
= inQueue
.take();
83 while (event
.getTimestampValue() != -1) {
85 event
= inQueue
.take();
87 /* We've received the last event, clean up */
90 } catch (InterruptedException e
) {
91 /* We've been interrupted abnormally */
92 System
.out
.println("Event handler interrupted!"); //$NON-NLS-1$
97 private void closeStateSystem() {
98 /* Close the History system, if there is one */
100 ss
.closeHistory(currentEvent
.getTimestamp().getValue());
101 } catch (TimeRangeException e
) {
103 * Since we're using currentEvent.getTimestamp, this shouldn't
110 private void processEvent(CtfTmfEvent event
) {
111 currentEvent
= event
;
112 ITmfEventField content
= event
.getContent();
113 String eventName
= event
.getEventName();
115 long ts
= event
.getTimestamp().getValue();
117 ITmfStateValue value
;
118 Integer eventCpu
= event
.getCPU();
119 Integer currentCPUNode
, currentThreadNode
, tidNode
;
121 /* Adjust the current nodes Vectors if we see a new CPU in an event */
122 if (eventCpu
>= currentCPUNodes
.size()) {
123 /* We need to add this node to the vector */
124 for (Integer i
= currentCPUNodes
.size(); i
< eventCpu
+ 1; i
++) {
125 quark
= ss
.getQuarkRelativeAndAdd(cpusNode
, i
.toString());
126 currentCPUNodes
.add(quark
);
128 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, Attributes
.UNKNOWN
);
129 currentThreadNodes
.add(quark
);
133 currentCPUNode
= currentCPUNodes
.get(eventCpu
);
134 currentThreadNode
= currentThreadNodes
.get(eventCpu
);
135 assert (currentCPUNode
!= null);
136 assert (currentThreadNode
!= null);
140 * Feed event to the history system if it's known to cause a state
143 switch (getEventIndex(eventName
)) {
145 case 1: // "exit_syscall":
146 /* Fields: int64 ret */
148 /* Clear the current system call on the process */
149 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
150 value
= TmfStateValue
.nullValue();
151 ss
.modifyAttribute(ts
, value
, quark
);
153 /* Put the process' status back to user mode */
154 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
155 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_RUN_USERMODE
);
156 ss
.modifyAttribute(ts
, value
, quark
);
160 case 2: // "irq_handler_entry":
161 /* Fields: int32 irq, string name */
163 Integer irqId
= ((Long
) content
.getField(LttngStrings
.IRQ
).getValue()).intValue();
165 /* Mark this IRQ as active in the resource tree.
166 * The state value = the CPU on which this IRQ is sitting */
167 quark
= ss
.getQuarkRelativeAndAdd(irqsNode
, irqId
.toString());
168 value
= TmfStateValue
.newValueInt(event
.getCPU());
169 ss
.modifyAttribute(ts
, value
, quark
);
171 /* Change the status of the running process to interrupted */
172 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
173 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_INTERRUPTED
);
174 ss
.modifyAttribute(ts
, value
, quark
);
178 case 3: // "irq_handler_exit":
179 /* Fields: int32 irq, int32 ret */
181 Integer irqId
= ((Long
) content
.getField(LttngStrings
.IRQ
).getValue()).intValue();
183 /* Put this IRQ back to inactive in the resource tree */
184 quark
= ss
.getQuarkRelativeAndAdd(irqsNode
, irqId
.toString());
185 value
= TmfStateValue
.nullValue();
186 ss
.modifyAttribute(ts
, value
, quark
);
188 /* Set the previous process back to running */
189 setProcessToRunning(ts
, currentThreadNode
);
193 case 4: // "softirq_entry":
194 /* Fields: int32 vec */
196 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
198 /* Mark this SoftIRQ as active in the resource tree.
199 * The state value = the CPU on which this SoftIRQ is processed */
200 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
201 value
= TmfStateValue
.newValueInt(event
.getCPU());
202 ss
.modifyAttribute(ts
, value
, quark
);
204 /* Change the status of the running process to interrupted */
205 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
206 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_INTERRUPTED
);
207 ss
.modifyAttribute(ts
, value
, quark
);
211 case 5: // "softirq_exit":
212 /* Fields: int32 vec */
214 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
216 /* Put this SoftIRQ back to inactive (= -1) in the resource tree */
217 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
218 value
= TmfStateValue
.nullValue();
219 ss
.modifyAttribute(ts
, value
, quark
);
221 /* Set the previous process back to running */
222 setProcessToRunning(ts
, currentThreadNode
);
226 case 6: // "softirq_raise":
227 /* Fields: int32 vec */
229 Integer softIrqId
= ((Long
) content
.getField(LttngStrings
.VEC
).getValue()).intValue();
231 /* Mark this SoftIRQ as *raised* in the resource tree.
232 * State value = -2 */
233 quark
= ss
.getQuarkRelativeAndAdd(softIrqsNode
, softIrqId
.toString());
234 value
= TmfStateValue
.newValueInt(Attributes
.SOFT_IRQ_RAISED
);
235 ss
.modifyAttribute(ts
, value
, quark
);
239 case 7: // "sched_switch":
241 * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 prev_state,
242 * string next_comm, int32 next_tid, int32 next_prio
245 Integer prevTid
= ((Long
) content
.getField(LttngStrings
.PREV_TID
).getValue()).intValue();
246 //Long prevState = (Long) content.getField(LttngStrings.PREV_STATE).getValue();
248 String nextProcessName
= (String
) content
.getField(LttngStrings
.NEXT_COMM
).getValue();
249 Integer nextTid
= ((Long
) content
.getField(LttngStrings
.NEXT_TID
).getValue()).intValue();
251 /* Update the currentThreadNodes pointer */
252 Integer newCurrentThreadNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, nextTid
.toString());
253 currentThreadNodes
.set(eventCpu
, newCurrentThreadNode
);
256 * Set the status of the process that got scheduled out, but
257 * only in the case where that process is currently active.
259 Integer formerThreadNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, prevTid
.toString());
260 quark
= ss
.getQuarkRelativeAndAdd(formerThreadNode
, Attributes
.EXEC_NAME
);
261 value
= ss
.queryOngoingState(quark
);
262 if (!value
.isNull()) {
263 quark
= ss
.getQuarkRelativeAndAdd(formerThreadNode
, Attributes
.STATUS
);
264 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_WAIT
);
265 ss
.modifyAttribute(ts
, value
, quark
);
268 /* Set the status of the new scheduled process */
269 setProcessToRunning(ts
, newCurrentThreadNode
);
271 /* Set the exec name of the new process */
272 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.EXEC_NAME
);
273 value
= TmfStateValue
.newValueString(nextProcessName
);
274 ss
.modifyAttribute(ts
, value
, quark
);
277 * Check if we need to set the syscall state and the PPID of
278 * the new process (in case we haven't seen this process before)
280 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.SYSTEM_CALL
);
281 if (quark
== ss
.getNbAttributes()) { /* Did we just add this attribute? */
282 value
= TmfStateValue
.nullValue();
283 ss
.modifyAttribute(ts
, value
, quark
);
285 quark
= ss
.getQuarkRelativeAndAdd(newCurrentThreadNode
, Attributes
.PPID
);
286 if (quark
== ss
.getNbAttributes()) {
287 value
= TmfStateValue
.nullValue();
288 ss
.modifyAttribute(ts
, value
, quark
);
291 /* Set the current scheduled process on the relevant CPU */
292 quark
= ss
.getQuarkRelativeAndAdd(currentCPUNode
, Attributes
.CURRENT_THREAD
);
293 value
= TmfStateValue
.newValueInt(nextTid
);
294 ss
.modifyAttribute(ts
, value
, quark
);
298 case 8: // "sched_process_fork":
299 /* Fields: string parent_comm, int32 parent_tid,
300 * string child_comm, int32 child_tid */
302 // String parentProcessName = (String)
303 // event.getFieldValue("parent_comm");
304 String childProcessName
;
305 childProcessName
= (String
) content
.getField(LttngStrings
.CHILD_COMM
).getValue();
306 // assert ( parentProcessName.equals(childProcessName) );
308 Integer parentTid
= ((Long
) content
.getField(LttngStrings
.PARENT_TID
).getValue()).intValue();
309 Integer childTid
= ((Long
) content
.getField(LttngStrings
.CHILD_TID
).getValue()).intValue();
311 tidNode
= ss
.getQuarkRelativeAndAdd(threadsNode
, childTid
.toString());
313 /* Assign the PPID to the new process */
314 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.PPID
);
315 value
= TmfStateValue
.newValueInt(parentTid
);
316 ss
.modifyAttribute(ts
, value
, quark
);
318 /* Set the new process' exec_name */
319 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.EXEC_NAME
);
320 value
= TmfStateValue
.newValueString(childProcessName
);
321 ss
.modifyAttribute(ts
, value
, quark
);
323 /* Set the new process' status */
324 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.STATUS
);
325 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_WAIT
);
326 ss
.modifyAttribute(ts
, value
, quark
);
328 /* Set the process' syscall state */
329 quark
= ss
.getQuarkRelativeAndAdd(tidNode
, Attributes
.SYSTEM_CALL
);
330 value
= TmfStateValue
.nullValue();
331 ss
.modifyAttribute(ts
, value
, quark
);
335 case 9: // "sched_process_exit":
336 /* Fields: string comm, int32 tid, int32 prio */
338 String processName
= (String
) content
.getField(LttngStrings
.COMM
).getValue();
339 Integer tid
= ((Long
) content
.getField(LttngStrings
.TID
).getValue()).intValue();
341 /* Update the process' name, if we don't have it */
342 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, tid
.toString(), Attributes
.EXEC_NAME
);
343 value
= TmfStateValue
.newValueString(processName
);
344 ss
.updateOngoingState(value
, quark
);
347 * Remove the process and all its sub-attributes from the
350 quark
= ss
.getQuarkRelativeAndAdd(threadsNode
, tid
.toString());
351 ss
.removeAttribute(ts
, quark
);
355 case 10: // "sched_process_free":
356 /* Fields: string comm, int32 tid, int32 prio */
359 // FIXME In CTF it's as "syscall_exec". Will have to be adapted.
360 // case LTT_EVENT_EXEC:
361 // filename = new String((byte[]) event.getField(0));
363 // /* Change the Exec_name of the process */
364 // quark = ss.getQuarkRelativePath(true, currentThreadNode,
366 // ss.modifyAttribute(ts, filename, quark);
370 /* Other event types not covered by the main switch */
372 if (eventName
.startsWith(LttngStrings
.SYSCALL_PREFIX
)
373 || eventName
.startsWith(LttngStrings
.COMPAT_SYSCALL_PREFIX
)) {
375 * This is a replacement for the old sys_enter event. Now
376 * syscall names are listed into the event type
379 /* Assign the new system call to the process */
380 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
381 value
= TmfStateValue
.newValueString(eventName
);
382 ss
.modifyAttribute(ts
, value
, quark
);
384 /* Put the process in system call mode */
385 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
386 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_RUN_SYSCALL
);
387 ss
.modifyAttribute(ts
, value
, quark
);
391 } // End of big switch
397 /* Number of events of each type, globally */
398 // quark = ss.getQuarkAbsoluteAndAdd(Attributes.STATISTICS,
399 // Attributes.EVENT_TYPES, eventName);
400 // ss.incrementAttribute(ts, quark);
402 /* Number of events per CPU */
403 // quark = ss.getQuarkRelativeAndAdd(currentCPUNode,
404 // Attributes.STATISTICS, Attributes.EVENT_TYPES, eventName);
405 // ss.incrementAttribute(ts, quark);
407 /* Number of events per process */
408 // quark = ss.getQuarkRelativeAndAdd(currentThreadNode,
409 // Attributes.STATISTICS, Attributes.EVENT_TYPES, eventName);
410 // ss.incrementAttribute(ts, quark);
412 } catch (AttributeNotFoundException ae
) {
414 * This would indicate a problem with the logic of the manager here,
415 * so it shouldn't happen.
417 ae
.printStackTrace();
419 } catch (TimeRangeException tre
) {
421 * This would happen if the events in the trace aren't ordered
422 * chronologically, which should never be the case ...
424 System
.err
.println("TimeRangeExcpetion caught in the state system's event manager."); //$NON-NLS-1$
425 System
.err
.println("Are the events in the trace correctly ordered?"); //$NON-NLS-1$
426 tre
.printStackTrace();
428 } catch (StateValueTypeException sve
) {
430 * This would happen if we were trying to push/pop attributes not of
431 * type integer. Which, once again, should never happen.
433 sve
.printStackTrace();
437 private void setupCommonLocations() {
438 cpusNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.CPUS
);
439 threadsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.THREADS
);
440 irqsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.RESOURCES
, Attributes
.IRQS
);
441 softIrqsNode
= ss
.getQuarkAbsoluteAndAdd(Attributes
.RESOURCES
, Attributes
.SOFT_IRQS
);
444 private static HashMap
<String
, Integer
> fillEventNames() {
446 * TODO Replace with straight strings in the switch/case once we move to
449 HashMap
<String
, Integer
> map
= new HashMap
<String
, Integer
>();
451 map
.put(LttngStrings
.EXIT_SYSCALL
, 1);
452 map
.put(LttngStrings
.IRQ_HANDLER_ENTRY
, 2);
453 map
.put(LttngStrings
.IRQ_HANDLER_EXIT
, 3);
454 map
.put(LttngStrings
.SOFTIRQ_ENTRY
, 4);
455 map
.put(LttngStrings
.SOFTIRQ_EXIT
, 5);
456 map
.put(LttngStrings
.SOFTIRQ_RAISE
, 6);
457 map
.put(LttngStrings
.SCHED_SWITCH
, 7);
458 map
.put(LttngStrings
.SCHED_PROCESS_FORK
, 8);
459 map
.put(LttngStrings
.SCHED_PROCESS_EXIT
, 9);
460 map
.put(LttngStrings
.SCHED_PROCESS_FREE
, 10);
465 private int getEventIndex(String eventName
) {
466 Integer ret
= knownEventNames
.get(eventName
);
467 return (ret
!= null) ? ret
: -1;
471 * When we want to set a process back to a "running" state, first check
472 * its current System_call attribute. If there is a system call active, we
473 * put the process back in the syscall state. If not, we put it back in
476 private void setProcessToRunning(long ts
, int currentThreadNode
)
477 throws AttributeNotFoundException
, TimeRangeException
,
478 StateValueTypeException
{
480 ITmfStateValue value
;
482 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.SYSTEM_CALL
);
483 if (ss
.queryOngoingState(quark
).isNull()) {
484 /* We were in user mode before the interruption */
485 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_RUN_USERMODE
);
487 /* We were previously in kernel mode */
488 value
= TmfStateValue
.newValueInt(Attributes
.STATUS_RUN_SYSCALL
);
490 quark
= ss
.getQuarkRelativeAndAdd(currentThreadNode
, Attributes
.STATUS
);
491 ss
.modifyAttribute(ts
, value
, quark
);