/******************************************************************************* * 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: * François Rajotte - Initial API and implementation * Geneviève Bastien - Revision of the initial implementation *******************************************************************************/ package org.eclipse.tracecompass.analysis.os.linux.core.cpuusage; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout; import org.eclipse.tracecompass.internal.analysis.os.linux.core.Activator; import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; import org.eclipse.tracecompass.statesystem.core.StateSystemBuilderUtils; import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; 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.statesystem.AbstractTmfStateProvider; import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; /** * Creates a state system with the total time spent on CPU for each thread and * for each CPU from a kernel trace. * * This state system in itself keeps the total time on CPU since last time the * process was scheduled out. The state system queries will only be accurate * when the process is not in a running state. To have exact CPU usage when * running, this state system needs to be used along the LTTng Kernel analysis. * * It requires only the 'sched_switch' events enabled on the trace. * * Attribute tree: * * <pre> * |- CPUS * | |- <CPU number> * | | |- <TID> -> cumulative time spent on the source CPU by the currently running thread (ns) * </pre> * * @author François Rajotte */ public class KernelCpuUsageStateProvider extends AbstractTmfStateProvider { private static final int VERSION = 2; /* For each CPU, maps the last time a thread was scheduled in */ private final Map<Integer, Long> fLastStartTimes = new HashMap<>(); private final long fTraceStart; private final IKernelAnalysisEventLayout fLayout; /** * Constructor * * @param trace * The trace from which to get the CPU usage * @param layout * The event layout to use for this state provider. */ public KernelCpuUsageStateProvider(ITmfTrace trace, IKernelAnalysisEventLayout layout) { super(trace, "Kernel CPU usage"); //$NON-NLS-1$ fTraceStart = trace.getStartTime().getValue(); fLayout = layout; } // ------------------------------------------------------------------------ // ITmfStateProvider // ------------------------------------------------------------------------ @Override public int getVersion() { return VERSION; } @Override public KernelCpuUsageStateProvider getNewInstance() { return new KernelCpuUsageStateProvider(this.getTrace(), this.fLayout); } @Override protected void eventHandle(@Nullable ITmfEvent event) { if (event == null) { return; } final String eventName = event.getName(); if (eventName.equals(fLayout.eventSchedSwitch())) { Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event); if (cpu == null) { /* We couldn't find any CPU information, ignore this event */ return; } /* * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 * prev_state, string next_comm, int32 next_tid, int32 next_prio */ ITmfEventField content = event.getContent(); long ts = event.getTimestamp().getValue(); Long prevTid = (Long) content.getField(fLayout.fieldPrevTid()).getValue(); try { final ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder()); Integer currentCPUNode = ss.getQuarkRelativeAndAdd(getNodeCPUs(ss), cpu.toString()); /* * This quark contains the value of the cumulative time spent on * the source CPU by the currently running thread */ Integer cumulativeTimeQuark = ss.getQuarkRelativeAndAdd(currentCPUNode, prevTid.toString()); Long startTime = fLastStartTimes.get(cpu); /* * If start time is null, we haven't seen the start of the * process, so we assume beginning of the trace */ if (startTime == null) { startTime = fTraceStart; } /* * Modify cumulative time for this CPU/TID combo: The total time * changes when the process is scheduled out. Nothing happens * when the process is scheduled in. */ StateSystemBuilderUtils.incrementAttributeLong(ss, ts, cumulativeTimeQuark, ts - startTime); fLastStartTimes.put(cpu, ts); } catch (AttributeNotFoundException e) { Activator.getDefault().logError("Attribute not found in LttngKernelCpuStateProvider", e); //$NON-NLS-1$ } } } /* Shortcut for the "current CPU" attribute node */ private static int getNodeCPUs(ITmfStateSystemBuilder ssb) { return ssb.getQuarkAbsoluteAndAdd(Attributes.CPUS); } }