| /******************************************************************************* |
| * 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 java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.List; |
| |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.fused.FusedAttributes; |
| 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.statesystem.core.exceptions.StateValueTypeException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
| |
| /** |
| * Utility methods to retrieve information from either the events or the state |
| * system |
| * |
| * @author Cédric Biancheri |
| */ |
| public class FusedVMEventHandlerUtils { |
| |
| private FusedVMEventHandlerUtils() { |
| } |
| |
| private static int getNodeCPUs(ITmfStateSystemBuilder ssb) { |
| return ssb.getQuarkAbsoluteAndAdd(FusedAttributes.CPUS); |
| } |
| |
| private static int getNodeThreads(ITmfStateSystemBuilder ssb, String machineName) { |
| return ssb.getQuarkAbsoluteAndAdd(FusedAttributes.THREADS, machineName); |
| } |
| |
| /** |
| * Get the node Machines |
| * |
| * @param ssb |
| * the state system |
| * @return the quark |
| */ |
| static int getMachinesNode(ITmfStateSystemBuilder ssb) { |
| return ssb.getQuarkAbsoluteAndAdd(FusedAttributes.HOSTS); |
| } |
| |
| /** |
| * Return the cpu quark |
| * |
| * @param cpuNumber |
| * number of the cpu |
| * @param ss |
| * the state system |
| * @return the quark |
| */ |
| static int getCurrentCPUNode(Integer cpuNumber, ITmfStateSystemBuilder ss) { |
| return ss.getQuarkRelativeAndAdd(getNodeCPUs(ss), cpuNumber.toString()); |
| } |
| |
| /** |
| * Get quark to current thread of a cpu |
| * |
| * @param cpuNumber |
| * number of the cpu |
| * @param ss |
| * the state system |
| * @return the quark |
| */ |
| public static int getCurrentThreadNode(Integer cpuNumber, ITmfStateSystemBuilder ss) { |
| /* |
| * Shortcut for the "current thread" attribute node. It requires |
| * querying the current CPU's current thread. |
| */ |
| int quark = ss.getQuarkRelativeAndAdd(getCurrentCPUNode(cpuNumber, ss), FusedAttributes.CURRENT_THREAD); |
| Object value = ss.queryOngoing(quark); |
| int thread = (value instanceof Integer) ? (int) value : -1; |
| quark = ss.getQuarkRelativeAndAdd(getCurrentCPUNode(cpuNumber, ss), FusedAttributes.MACHINE_NAME); |
| String machineName = (String) ss.queryOngoing(quark); |
| return ss.getQuarkRelativeAndAdd(getNodeThreads(ss, machineName), buildThreadAttributeName(thread, cpuNumber)); |
| } |
| |
| /** |
| * Build the thread attribute name. |
| * |
| * For all threads except "0" this is the string representation of the |
| * threadId. For thread "0" which is the idle thread and can be running |
| * concurrently on multiple CPUs, append "_cpuId". |
| * |
| * @param threadId |
| * the thread id |
| * @param cpuId |
| * the cpu id |
| * |
| * @return the thread attribute name null if the threadId is zero and the |
| * cpuId is null |
| */ |
| public static @Nullable String buildThreadAttributeName(int threadId, @Nullable Integer cpuId) { |
| |
| if (threadId == 0) { |
| if (cpuId == null) { |
| return null; |
| } |
| return FusedAttributes.THREAD_0_PREFIX + String.valueOf(cpuId); |
| } |
| |
| return String.valueOf(threadId); |
| } |
| |
| /** |
| * Get the IRQs node |
| * |
| * @param cpuNumber |
| * the cpu core |
| * @param ss |
| * the state system |
| * @return the IRQ node quark |
| */ |
| public static int getNodeIRQs(int cpuNumber, ITmfStateSystemBuilder ss) { |
| return ss.getQuarkAbsoluteAndAdd(FusedAttributes.CPUS, Integer.toString(cpuNumber), FusedAttributes.IRQS); |
| } |
| |
| /** |
| * Get the timestamp of the event |
| * |
| * @param event |
| * the event containing the timestamp |
| * |
| * @return the timestamp in long format |
| */ |
| public static long getTimestamp(ITmfEvent event) { |
| return event.getTimestamp().toNanos(); |
| } |
| |
| /** |
| * When we want to set a process back to a "running" state, first check its |
| * current System_call attribute. If there is a system call active, we put |
| * the process back in the syscall state. If not, we put it back in user |
| * mode state. |
| * |
| * @param timestamp |
| * the time in the state system of the change |
| * @param currentThreadNode |
| * The current thread node |
| * @param ssb |
| * the state system |
| * @throws TimeRangeException |
| * the time is out of range |
| * @throws StateValueTypeException |
| * the attribute was not set with int values |
| */ |
| public static void setProcessToRunning(long timestamp, int currentThreadNode, ITmfStateSystemBuilder ssb) |
| throws TimeRangeException, StateValueTypeException { |
| int quark; |
| Object value; |
| |
| quark = ssb.getQuarkRelativeAndAdd(currentThreadNode, FusedAttributes.SYSTEM_CALL); |
| if (ssb.queryOngoingState(quark).isNull()) { |
| /* We were in user mode before the interruption */ |
| value = StateValues.PROCESS_STATUS_RUN_USERMODE; |
| } else { |
| /* We were previously in kernel mode */ |
| value = StateValues.PROCESS_STATUS_RUN_SYSCALL; |
| } |
| quark = ssb.getQuarkRelativeAndAdd(currentThreadNode, FusedAttributes.STATUS); |
| ssb.modifyAttribute(timestamp, value, quark); |
| } |
| |
| /** |
| * Reset the CPU's status when it's coming out of an interruption. |
| * |
| * @param timestamp |
| * the time when the status of the cpu is "leaving irq" |
| * @param cpuNumber |
| * the cpu returning to its previous state |
| * |
| * @param ssb |
| * State system |
| * @throws StateValueTypeException |
| * the attribute is not set as an int |
| * @throws TimeRangeException |
| * the time is out of range |
| */ |
| public static void cpuExitInterrupt(long timestamp, Integer cpuNumber, ITmfStateSystemBuilder ssb) |
| throws StateValueTypeException, TimeRangeException { |
| int quark; |
| int currentCPUNode = getCurrentCPUNode(cpuNumber, ssb); |
| |
| quark = ssb.getQuarkRelativeAndAdd(currentCPUNode, FusedAttributes.STATUS); |
| Object value = getCpuStatus(ssb, currentCPUNode); |
| ssb.modifyAttribute(timestamp, value, quark); |
| } |
| |
| /** |
| * Get the ongoing Status state of a CPU. |
| * |
| * This will look through the states of the |
| * |
| * <ul> |
| * <li>IRQ</li> |
| * <li>Soft IRQ</li> |
| * <li>Process</li> |
| * </ul> |
| * |
| * under the CPU, giving priority to states higher in the list. If the state |
| * is a null value, we continue looking down the list. |
| * |
| * @param ssb |
| * The state system |
| * @param cpuQuark |
| * The *quark* of the CPU we are looking for. Careful, this is |
| * NOT the CPU number (or attribute name)! |
| * @return The state value that represents the status of the given CPU |
| */ |
| private static @Nullable Integer getCpuStatus(ITmfStateSystemBuilder ssb, int cpuQuark) { |
| |
| /* Check if there is a IRQ running */ |
| int irqQuarks = ssb.getQuarkRelativeAndAdd(cpuQuark, FusedAttributes.IRQS); |
| List<Integer> irqs = ssb.getSubAttributes(irqQuarks, false); |
| for (Integer quark : irqs) { |
| final Object irqState = ssb.queryOngoing(quark.intValue()); |
| if (irqState instanceof Integer) { |
| return (Integer) irqState; |
| } |
| } |
| |
| /* Check if there is a soft IRQ running */ |
| int softIrqQuarks = ssb.getQuarkRelativeAndAdd(cpuQuark, FusedAttributes.SOFT_IRQS); |
| List<Integer> softIrqs = ssb.getSubAttributes(softIrqQuarks, false); |
| for (Integer quark : softIrqs) { |
| final Object softIrqState = ssb.queryOngoing(quark.intValue()); |
| if (softIrqState instanceof Integer) { |
| return (Integer) softIrqState; |
| } |
| } |
| |
| /* |
| * Check if there is a thread running. If not, report IDLE. If there is, |
| * report the running state of the thread (usermode or system call). |
| */ |
| int currentThreadQuark = ssb.getQuarkRelativeAndAdd(cpuQuark, FusedAttributes.CURRENT_THREAD); |
| Object currentThreadState = ssb.queryOngoing(currentThreadQuark); |
| if (!(currentThreadState instanceof Integer)) { |
| return null; |
| } |
| int tid = (Integer) currentThreadState; |
| if (tid == 0) { |
| return StateValues.CPU_STATUS_IDLE; |
| } |
| int currentMachineQuark = ssb.getQuarkRelativeAndAdd(cpuQuark, FusedAttributes.MACHINE_NAME); |
| String machineName = (String) ssb.queryOngoing(currentMachineQuark); |
| int threadSystemCallQuark = ssb.getQuarkRelativeAndAdd(getNodeThreads(ssb, machineName), Integer.toString(tid), FusedAttributes.SYSTEM_CALL); |
| return (ssb.queryOngoingState(threadSystemCallQuark).isNull() ? StateValues.CPU_STATUS_RUN_USERMODE : StateValues.CPU_STATUS_RUN_SYSCALL); |
| } |
| |
| /** |
| * Get a machine CPUs node. This node corresponds to the CPUs available to |
| * the machine, ie in the case of a virtual machine, the virtual CPUs. |
| * |
| * @param ssq |
| * the state system |
| * @param hostId |
| * the host ID of the machine |
| * @return the quark |
| */ |
| public static int getMachineCPUsNode(ITmfStateSystemBuilder ssq, String hostId) { |
| return ssq.getQuarkAbsoluteAndAdd(FusedAttributes.HOSTS, hostId, FusedAttributes.CPUS); |
| } |
| |
| /** |
| * Get a machine pCPUs node. This node corresponds to the physical CPUs used |
| * by the machine, ie, in the case of a virtual machine, the CPUs on the |
| * host machine. |
| * |
| * @param ssq |
| * the state system |
| * @param hostId |
| * the host ID of the machine |
| * @return the quark |
| */ |
| public static int getMachinepCPUsNode(ITmfStateSystemBuilder ssq, String hostId) { |
| return ssq.getQuarkAbsoluteAndAdd(FusedAttributes.HOSTS, hostId, FusedAttributes.PCPUS); |
| } |
| |
| /** |
| * Get the threads node |
| * |
| * @param ss |
| * the state system |
| * @return the threads quark |
| */ |
| public static int getNodeThreads(ITmfStateSystemBuilder ss) { |
| return ss.getQuarkAbsoluteAndAdd(FusedAttributes.THREADS); |
| } |
| |
| /** |
| * Save container thread ID by adding it |
| * |
| * @param ss |
| * the state system |
| * @param quark |
| * the quark |
| * @param tid |
| * the tid |
| * @return the matching quark |
| */ |
| public static int saveContainerThreadID(ITmfStateSystemBuilder ss, int quark, int tid) { |
| return ss.getQuarkRelativeAndAdd(quark, FusedAttributes.THREADS, Integer.toString(tid)); |
| } |
| |
| /** |
| * Get the Soft IRQs node |
| * |
| * @param cpuNumber |
| * the cpu core |
| * @param ss |
| * the state system |
| * @return the Soft IRQ node quark |
| */ |
| public static int getNodeSoftIRQs(int cpuNumber, ITmfStateSystemBuilder ss) { |
| return ss.getQuarkAbsoluteAndAdd(FusedAttributes.CPUS, Integer.toString(cpuNumber), FusedAttributes.SOFT_IRQS); |
| } |
| |
| /** |
| * Get the namespaces for a thread |
| * |
| * @param ss |
| * The state system |
| * @param threadQuark |
| * The quark of the thread |
| * @return The list of namespaces the thread is part of |
| */ |
| public static List<Long> getProcessNSIDs(ITmfStateSystemBuilder ss, int threadQuark) { |
| List<Long> namespaces = new ArrayList<>(); |
| int maxLvQuark = ss.optQuarkRelative(threadQuark, FusedAttributes.NS_MAX_LEVEL); |
| if (maxLvQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { |
| return namespaces; |
| } |
| Object value = ss.queryOngoing(maxLvQuark); |
| if (value == null) { |
| return namespaces; |
| } |
| int nsMaxLevel = (int) value; |
| if (nsMaxLevel > 1) { |
| int currentLevel = 1; |
| int vtidQuark = threadQuark; |
| while (currentLevel < nsMaxLevel) { |
| vtidQuark = ss.optQuarkRelative(vtidQuark, FusedAttributes.VTID); |
| if (vtidQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { |
| return namespaces; |
| } |
| int namespaceIDQuark = ss.optQuarkRelative(vtidQuark, FusedAttributes.NS_INUM); |
| if (namespaceIDQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { |
| return namespaces; |
| } |
| currentLevel++; |
| long namespaceID = ss.queryOngoingState(namespaceIDQuark).unboxLong(); |
| namespaces.add(namespaceID); |
| } |
| |
| } |
| return namespaces; |
| } |
| |
| /** |
| * Method for debug purpose. Transform timestamp to something readable: |
| * hh:mm:ss |
| * |
| * @param time |
| * the time |
| * @return a readable formatted string |
| */ |
| public static String formatTime(long time) { |
| |
| return formatTimeAbs(time); |
| } |
| |
| private static String formatNs(long srcTime) { |
| StringBuffer str = new StringBuffer(); |
| long ns = Math.abs(srcTime % 1000000000); |
| String nanos = Long.toString(ns); |
| str.append("000000000".substring(nanos.length())); //$NON-NLS-1$ |
| str.append(nanos); |
| return str.substring(0, 9); |
| } |
| |
| private static String formatTimeAbs(long time) { |
| StringBuffer str = new StringBuffer(); |
| |
| // format time from nanoseconds to calendar time HH:MM:SS |
| SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$ |
| String stime = timeFormat.format(new Date(time / 1000000)); |
| str.append(stime); |
| str.append('.'); |
| // append the Milliseconds, MicroSeconds and NanoSeconds as specified in |
| // the Resolution |
| str.append(formatNs(time)); |
| return str.toString(); |
| } |
| |
| } |