blob: a7347d966b6b32358775c736d95ad0f5e26a9062 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.fused.handlers;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout;
import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.fused.FusedAttributes;
import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.virtual.resources.LinuxValues;
import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.virtual.resources.StateValues;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
/**
* Handler for the process statedump event. It initializes the processes'
* namespaces and inserts the process in their proper namespace(s)
*
* @author Cédric Biancheri
*/
public class StateDumpContainerHandler extends VMKernelEventHandler {
private static final String TID_FIELD = "tid"; //$NON-NLS-1$
private static final String VTID_FIELD = "vtid"; //$NON-NLS-1$
private static final String PID_FIELD = "pid"; //$NON-NLS-1$
private static final String VPID_FIELD = "vpid"; //$NON-NLS-1$
private static final String PPID_FIELD = "ppid"; //$NON-NLS-1$
private static final String VPPID_FIELD = "vppid"; //$NON-NLS-1$
private static final String NAME_FIELD = "name"; //$NON-NLS-1$
private static final String STATUS_FIELD = "status"; //$NON-NLS-1$
private static final String NS_LEVEL_FIELD = "ns_level"; //$NON-NLS-1$
private static final String NS_INUM_FIELD = "ns_inum"; //$NON-NLS-1$
/**
* Constructor with a layout
*
* @param layout
* The event layout for this trace
* @param sp
* The state provider
*/
public StateDumpContainerHandler(IKernelAnalysisEventLayout layout, FusedVirtualMachineStateProvider sp) {
super(layout, sp);
}
@Override
public void handleEvent(ITmfStateSystemBuilder ss, ITmfEvent event) {
int layerNode = createLevels(ss, event);
if (layerNode != ITmfStateSystem.INVALID_ATTRIBUTE) {
fillLevel(ss, event, layerNode);
}
}
/**
* Create all the levels of containers for a process inside the state
* system.
*
* @param ss
* The state system
* @param event
* The statedump_process event
* @return The quark of the deepest level
*/
public static int createLevels(ITmfStateSystemBuilder ss, ITmfEvent event) {
Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event);
ITmfEventField content = event.getContent();
Long tid = content.getFieldValue(Long.class, TID_FIELD);
Long vtid = content.getFieldValue(Long.class, VTID_FIELD);
Long nsLevel = content.getFieldValue(Long.class, NS_LEVEL_FIELD);
if (tid == null || vtid == null || nsLevel == null) {
return ITmfStateSystem.INVALID_ATTRIBUTE;
}
long ts = event.getTimestamp().getValue();
String hostId = event.getTrace().getHostId();
String threadAttributeName = FusedVMEventHandlerUtils.buildThreadAttributeName(tid.intValue(), cpu);
if (threadAttributeName == null) {
return ITmfStateSystem.INVALID_ATTRIBUTE;
}
int threadNode = ss.getQuarkRelativeAndAdd(FusedVMEventHandlerUtils.getNodeThreads(ss), hostId, threadAttributeName);
int layerQuark = threadNode;
for (int i = 0; i < nsLevel; i++) {
/* While we can go deeper we create an other level */
layerQuark = ss.getQuarkRelativeAndAdd(layerQuark, FusedAttributes.VTID);
if (i + 1 == nsLevel) {
/*
* If the next layer is the last we can add the info contained
* in the event
*/
ss.modifyAttribute(ts, vtid.intValue(), layerQuark);
}
ss.getQuarkRelativeAndAdd(layerQuark, FusedAttributes.VPPID);
int quark = ss.getQuarkRelativeAndAdd(layerQuark, FusedAttributes.NS_LEVEL);
if (ss.queryOngoingState(quark).isNull()) {
/* If the value didn't exist previously, set it */
ss.modifyAttribute(ts, i + 1, quark);
}
}
return layerQuark;
}
/**
* Fill the first and last level of a thread node
*
* @param ss
* The state system
* @param event
* The statedump_process event
* @param layerNode
* The quark of the last level
*/
public static void fillLevel(ITmfStateSystemBuilder ss, ITmfEvent event, int layerNode) {
Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event);
ITmfEventField content = event.getContent();
long ts = event.getTimestamp().getValue();
int quark;
String hostId = event.getTrace().getHostId();
Long tid = content.getFieldValue(Long.class, TID_FIELD);
Long pid = content.getFieldValue(Long.class, PID_FIELD);
Long ppid = content.getFieldValue(Long.class, PPID_FIELD);
Long status = content.getFieldValue(Long.class, STATUS_FIELD);
String name = content.getFieldValue(String.class, NAME_FIELD);
Long vtid = content.getFieldValue(Long.class, VTID_FIELD);
Long vpid = content.getFieldValue(Long.class, VPID_FIELD);
Long vppid = content.getFieldValue(Long.class, VPPID_FIELD);
Long nsLevel = content.getFieldValue(Long.class, NS_LEVEL_FIELD);
Long nsInum = content.getFieldValue(Long.class, NS_INUM_FIELD);
if (tid == null || pid == null || ppid == null || status == null
|| name == null || vtid == null || vpid == null
|| vppid == null || nsLevel == null || nsInum == null) {
return;
}
String threadAttributeName = FusedVMEventHandlerUtils.buildThreadAttributeName(tid.intValue(), cpu);
if (threadAttributeName == null) {
return;
}
int threadNode = ss.getQuarkRelativeAndAdd(FusedVMEventHandlerUtils.getNodeThreads(ss), hostId, threadAttributeName);
/*
* Set the max level, only at level 0. This can be useful to know the
* depth of the hierarchy.
*/
quark = ss.getQuarkRelativeAndAdd(threadNode, FusedAttributes.NS_MAX_LEVEL);
if (ss.queryOngoingState(quark).isNull()) {
/*
* Events are coming from the deepest layers first so no need to
* update the ns_max_level.
*/
ss.modifyAttribute(ts, nsLevel.intValue() + 1, quark);
}
int maxLevel = ss.queryOngoingState(quark).unboxInt();
/*
* Set the process' status. Only for level 0.
*/
quark = ss.getQuarkRelativeAndAdd(threadNode, FusedAttributes.STATUS);
int processStatus = StateValues.PROCESS_STATUS_UNKNOWN;
if (ss.queryOngoing(quark) == null) {
switch (status.intValue()) {
case LinuxValues.STATEDUMP_PROCESS_STATUS_WAIT_CPU:
processStatus = StateValues.PROCESS_STATUS_WAIT_FOR_CPU;
break;
case LinuxValues.STATEDUMP_PROCESS_STATUS_WAIT:
/*
* We have no information on what the process is waiting on
* (unlike a sched_switch for example), so we will use the
* WAIT_UNKNOWN state instead of the "normal" WAIT_BLOCKED
* state.
*/
processStatus = StateValues.PROCESS_STATUS_WAIT_UNKNOWN;
break;
default:
processStatus = StateValues.PROCESS_STATUS_UNKNOWN;
}
ss.modifyAttribute(ts, processStatus, quark);
}
/*
* Set the process' name. Only for level 0.
*/
quark = ss.getQuarkRelativeAndAdd(threadNode, FusedAttributes.EXEC_NAME);
if (ss.queryOngoing(quark) == null) {
/* If the value didn't exist previously, set it */
ss.modifyAttribute(ts, name, quark);
}
String attributePpid = FusedAttributes.PPID;
/* Prepare the level if we are not in the root namespace */
if (nsLevel != 0) {
attributePpid = "VPPID"; //$NON-NLS-1$
}
/* Set the process' PPID */
quark = ss.getQuarkRelativeAndAdd(layerNode, attributePpid);
if (ss.queryOngoing(quark) == null) {
int setPpid;
int setVppid;
if (vpid.equals(vtid)) {
/* We have a process. Use the 'PPID' field. */
setVppid = vppid.intValue();
setPpid = ppid.intValue();
} else {
/*
* We have a thread, use the 'PID' field for the parent.
*/
setVppid = vpid.intValue();
setPpid = pid.intValue();
}
ss.modifyAttribute(ts, setVppid, quark);
if (nsLevel != 0) {
/* Set also for the root layer */
quark = ss.getQuarkRelativeAndAdd(threadNode, FusedAttributes.PPID);
if (ss.queryOngoing(quark) == null) {
ss.modifyAttribute(ts, setPpid, quark);
}
}
}
/* Set the namespace level */
quark = ss.getQuarkRelativeAndAdd(layerNode, FusedAttributes.NS_LEVEL);
if (ss.queryOngoingState(quark).isNull()) {
/* If the value didn't exist previously, set it */
ss.modifyAttribute(ts, nsLevel.intValue(), quark);
}
/* Set the namespace identification number */
quark = ss.getQuarkRelativeAndAdd(layerNode, FusedAttributes.NS_INUM);
if (ss.queryOngoingState(quark).isNull()) {
/* If the value didn't exist previously, set it */
ss.modifyAttribute(ts, nsInum, quark);
}
/* Save the namespace id somewhere so it can be reused */
quark = ss.getQuarkRelativeAndAdd(FusedVMEventHandlerUtils.getMachinesNode(ss), hostId, FusedAttributes.CONTAINERS, Long.toString(nsInum));
/* Save the tid in the container. We also keep the vtid */
quark = ss.getQuarkRelativeAndAdd(quark, FusedAttributes.THREADS, String.valueOf(tid));
ss.modifyAttribute(ts, vtid.intValue(), quark);
if (nsLevel != maxLevel - 1) {
/*
* We are not at the deepest level. So this namespace is the father
* of the namespace one layer deeper. We are going to tell him we
* found his father. That will make him happy.
*/
quark = ss.getQuarkRelativeAndAdd(layerNode, FusedAttributes.VTID, FusedAttributes.NS_INUM);
Long childNSInum = ss.queryOngoingState(quark).unboxLong();
if (childNSInum > 0) {
quark = ss.getQuarkRelativeAndAdd(FusedVMEventHandlerUtils.getMachinesNode(ss), hostId, FusedAttributes.CONTAINERS, Long.toString(childNSInum), FusedAttributes.PARENT);
ss.modifyAttribute(ss.getStartTime(), nsInum, quark);
}
}
if (nsLevel == 0) {
/* Root namespace => no parent */
quark = ss.getQuarkRelativeAndAdd(FusedVMEventHandlerUtils.getMachinesNode(ss), hostId, FusedAttributes.CONTAINERS, Long.toString(nsInum), FusedAttributes.PARENT);
ss.modifyAttribute(ss.getStartTime(), -1L, quark);
}
}
}