/******************************************************************************* * Copyright (c) 2014, 2015 É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 v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Mohamad Gebai - Initial API and implementation * Geneviève Bastien - Initial API and implementation *******************************************************************************/ package org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.module; import java.util.Comparator; import java.util.HashSet; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelThreadInformationProvider; import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VcpuStateValues; import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VmAttributes; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; import org.eclipse.tracecompass.statesystem.core.StateSystemUtils; import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; import org.eclipse.tracecompass.statesystem.core.interval.TmfStateInterval; import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type; import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment; import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperimentUtils; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.TreeMultimap; /** * Module for the virtual machine CPU analysis. It tracks the status of the * virtual CPUs for each guest of the experiment. * * @author Mohamad Gebai * @author Geneviève Bastien */ public class VirtualMachineCpuAnalysis extends TmfStateSystemAnalysisModule { /** The ID of this analysis module */ public static final String ID = "org.eclipse.tracecompass.lttng2.analysis.vm.core.VirtualMachineAnalysisModule"; //$NON-NLS-1$ // TODO: Update with event layout when requirements are back */ static final Set<String> REQUIRED_EVENTS = ImmutableSet.of( // LttngStrings.SCHED_SWITCH ); /* State value for a preempted virtual CPU */ private static final ITmfStateValue VCPU_PREEMPT_VALUE = TmfStateValue.newValueInt(VcpuStateValues.VCPU_PREEMPT); /** * Constructor */ public VirtualMachineCpuAnalysis() { super(); } @Override protected ITmfStateProvider createStateProvider() { ITmfTrace trace = getTrace(); if (!(trace instanceof TmfExperiment)) { throw new IllegalStateException(); } return new VirtualMachineStateProvider((TmfExperiment) trace); } @Override protected @NonNull StateSystemBackendType getBackendType() { return StateSystemBackendType.FULL; } @Override public String getHelpText() { return Messages.getMessage(Messages.VirtualMachineCPUAnalysis_Help); } @Override protected Iterable<IAnalysisModule> getDependentAnalyses() { Set<IAnalysisModule> modules = new HashSet<>(); /* Depends on the LTTng Kernel analysis modules */ for (ITmfTrace trace : TmfTraceManager.getTraceSet(getTrace())) { for (KernelAnalysisModule module : TmfTraceUtils.getAnalysisModulesOfClass(trace, KernelAnalysisModule.class)) { modules.add(module); } } return modules; } private static Multimap<Integer, ITmfStateInterval> createThreadMultimap() { /* * Create the multimap for threads with the appropriate comparator * objects for keys and values */ final Multimap<Integer, ITmfStateInterval> map = TreeMultimap.create( /* Key comparator. Keys do not have to be sorted, just use natural sorting*/ Comparator.naturalOrder(), /* Value comparator */ (arg0, arg1) -> { if (arg1.getStateValue() == VCPU_PREEMPT_VALUE && arg0.getStateValue() != VCPU_PREEMPT_VALUE) { /* * For VCPU_PREEMPT state values, the state has to be * after any other state that it overlaps, because those * intervals usually decorate the other intervals. */ if (((Long) arg0.getEndTime()).compareTo(arg1.getStartTime()) < 0) { return -1; } return ((Long) arg0.getStartTime()).compareTo(arg1.getEndTime()); } /* Otherwise, we use ordering by start time */ return (((Long) arg0.getStartTime()).compareTo(arg1.getStartTime())); }); return map; } /** * Get the status intervals for the threads from a virtual machine. Those * intervals are correlated with the data from the virtual CPU's preemption * status. * * This method uses the Linux Kernel Analysis data for the thread's status * intervals. * * @param vmQuark * The quark of the virtual machine * @param start * The start time of the period to get the intervals from * @param end * The end time of the period to get the intervals from * @param resolution * The resolution * @param monitor * A progress monitor for this task * @return A map of status intervals for the machine's threads, including * preempted intervals. Intervals from the thread status and the CPU * preemption status overlap and are ordered such that CPU * preemption intervals are after any interval they overlap with */ public Multimap<Integer, ITmfStateInterval> getUpdatedThreadIntervals(int vmQuark, long start, long end, long resolution, IProgressMonitor monitor) { final Multimap<Integer, ITmfStateInterval> map = createThreadMultimap(); ITmfStateSystem ss = getStateSystem(); if (ss == null) { return map; } ITmfTrace trace = getTrace(); if (!(trace instanceof TmfExperiment)) { return map; } String vmHostId = ss.getAttributeName(vmQuark); KernelAnalysisModule kernelModule = TmfExperimentUtils.getAnalysisModuleOfClassForHost((TmfExperiment) trace, vmHostId, KernelAnalysisModule.class); if (kernelModule == null) { return map; } /* * Initialize the map with the original status intervals from the kernel * module */ for (Integer tid : KernelThreadInformationProvider.getThreadIds(kernelModule)) { map.putAll(tid, KernelThreadInformationProvider.getStatusIntervalsForThread(kernelModule, tid, start, end, resolution, monitor)); if (monitor.isCanceled()) { return map; } } try { /* Correlate thread information with virtual CPU information */ for (Integer vcpuQuark : ss.getSubAttributes(vmQuark, false)) { Long virtualCPU = Long.parseLong(ss.getAttributeName(vcpuQuark)); Integer statusQuark = ss.getQuarkRelative(vcpuQuark, VmAttributes.STATUS); for (ITmfStateInterval cpuInterval : StateSystemUtils.queryHistoryRange(ss, statusQuark, start, end - 1, resolution, monitor)) { ITmfStateValue stateValue = cpuInterval.getStateValue(); if (stateValue.getType() == Type.INTEGER) { int value = stateValue.unboxInt(); /* * If the current CPU is either preempted or in * hypervisor mode, add preempted intervals to running * processes */ if ((value & (VcpuStateValues.VCPU_PREEMPT | VcpuStateValues.VCPU_VMM)) == 0) { continue; } Integer threadOnCpu = KernelThreadInformationProvider.getThreadOnCpu(kernelModule, virtualCPU, cpuInterval.getStartTime()); if (threadOnCpu != null) { map.put(threadOnCpu, new TmfStateInterval(cpuInterval.getStartTime(), cpuInterval.getEndTime(), threadOnCpu, VCPU_PREEMPT_VALUE)); } } } } } catch (AttributeNotFoundException | StateSystemDisposedException e) { } return map; } }