From dffc234fd51d4c247aeef9ff3596c0dea841abe8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Genevi=C3=A8ve=20Bastien?= Date: Mon, 17 Feb 2014 10:32:00 -0500 Subject: [PATCH] lttng: Add a view for the CPU usage analysis MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This view contains a tree viewer to show all threads spending time on the CPU in the time range, and an XY chart viewer to display the total CPU of all threads and the CPU usage of the currently selected from the tree viewer. Change-Id: Id96fa1005623601539fad10e28949691b70ea1df Signed-off-by: Geneviève Bastien Reviewed-on: https://git.eclipse.org/r/22522 Tested-by: Hudson CI Tested-by: Matthew Khouzam Reviewed-by: Matthew Khouzam --- .../cpuusage/LttngKernelCpuUsageAnalysis.java | 2 + .../.settings/org.eclipse.jdt.core.prefs | 2 +- .../META-INF/MANIFEST.MF | 1 + .../plugin.properties | 1 + .../plugin.xml | 15 + .../ui/views/cpuusage/CpuUsageComposite.java | 283 ++++++++++++++++++ .../ui/views/cpuusage/CpuUsageEntry.java | 86 ++++++ .../ui/views/cpuusage/CpuUsageView.java | 70 +++++ .../ui/views/cpuusage/CpuUsageXYViewer.java | 218 ++++++++++++++ .../kernel/ui/views/cpuusage/Messages.java | 43 +++ .../ui/views/cpuusage/messages.properties | 23 ++ 11 files changed, 743 insertions(+), 1 deletion(-) create mode 100644 org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageComposite.java create mode 100644 org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageEntry.java create mode 100644 org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageView.java create mode 100644 org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageXYViewer.java create mode 100644 org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/Messages.java create mode 100644 org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/messages.properties diff --git a/org.eclipse.linuxtools.lttng2.kernel.core/src/org/eclipse/linuxtools/lttng2/kernel/core/cpuusage/LttngKernelCpuUsageAnalysis.java b/org.eclipse.linuxtools.lttng2.kernel.core/src/org/eclipse/linuxtools/lttng2/kernel/core/cpuusage/LttngKernelCpuUsageAnalysis.java index 174b5da005..6a8a9f3c58 100644 --- a/org.eclipse.linuxtools.lttng2.kernel.core/src/org/eclipse/linuxtools/lttng2/kernel/core/cpuusage/LttngKernelCpuUsageAnalysis.java +++ b/org.eclipse.linuxtools.lttng2.kernel.core/src/org/eclipse/linuxtools/lttng2/kernel/core/cpuusage/LttngKernelCpuUsageAnalysis.java @@ -105,7 +105,9 @@ public class LttngKernelCpuUsageAnalysis extends TmfStateSystemAnalysisModule { * don't get TimeRange exceptions. */ long startTime = Math.max(start, cpuSs.getStartTime()); + startTime = Math.max(startTime, kernelSs.getStartTime()); long endTime = Math.min(end, cpuSs.getCurrentEndTime()); + endTime = Math.min(endTime, kernelSs.getCurrentEndTime()); long totalTime = 0; if (endTime < startTime) { return map; diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.linuxtools.lttng2.kernel.ui/.settings/org.eclipse.jdt.core.prefs index 5009ad9f24..b50d517d73 100644 --- a/org.eclipse.linuxtools.lttng2.kernel.ui/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/.settings/org.eclipse.jdt.core.prefs @@ -21,7 +21,7 @@ org.eclipse.jdt.core.compiler.problem.deadCode=error org.eclipse.jdt.core.compiler.problem.deprecation=warning org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=enabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=error +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=error diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/META-INF/MANIFEST.MF b/org.eclipse.linuxtools.lttng2.kernel.ui/META-INF/MANIFEST.MF index cd20f1627b..8764a3b2ee 100644 --- a/org.eclipse.linuxtools.lttng2.kernel.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/META-INF/MANIFEST.MF @@ -21,4 +21,5 @@ Export-Package: org.eclipse.linuxtools.internal.lttng2.kernel.ui;x-friends:="org org.eclipse.linuxtools.internal.lttng2.kernel.ui.viewers.events;x-internal:=true, org.eclipse.linuxtools.internal.lttng2.kernel.ui.views;x-friends:="org.eclipse.linuxtools.lttng2.kernel.ui.swtbot.tests", org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.controlflow;x-friends:="org.eclipse.linuxtools.lttng2.kernel.ui.swtbot.tests", + org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.cpuusage;x-friends:="org.eclipse.linuxtools.lttng2.kernel.ui.swtbot.tests", org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.resources;x-friends:="org.eclipse.linuxtools.lttng2.kernel.ui.swtbot.tests" diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.properties b/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.properties index 2d929a044c..0a53a64e67 100644 --- a/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.properties +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.properties @@ -18,6 +18,7 @@ kernel.perspective.name = LTTng Kernel controlflow.view.name = Control Flow resources.view.name = Resources +cpuusage.view.name = CPU Usage tracetype.type.kernel = LTTng Kernel Trace analysis.lttngkernel = LTTng Kernel Analysis diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.xml b/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.xml index 7416106ce2..de8ad03009 100644 --- a/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.xml +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/plugin.xml @@ -30,6 +30,14 @@ name="%resources.view.name" restorable="true"> + + @@ -63,6 +71,13 @@ id="org.eclipse.linuxtools.lttng2.kernel.analysis"> + + + + diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageComposite.java b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageComposite.java new file mode 100644 index 0000000000..27c88ef39f --- /dev/null +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageComposite.java @@ -0,0 +1,283 @@ +/******************************************************************************* + * Copyright (c) 2014 É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: + * Geneviève Bastien - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.cpuusage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.linuxtools.internal.lttng2.kernel.core.Attributes; +import org.eclipse.linuxtools.lttng2.kernel.core.analysis.LttngKernelAnalysisModule; +import org.eclipse.linuxtools.lttng2.kernel.core.cpuusage.LttngKernelCpuUsageAnalysis; +import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException; +import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException; +import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval; +import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem; +import org.eclipse.linuxtools.tmf.core.statesystem.TmfStateSystemAnalysisModule; +import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue; +import org.eclipse.linuxtools.tmf.ui.viewers.tree.AbstractTmfTreeViewer; +import org.eclipse.linuxtools.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider; +import org.eclipse.linuxtools.tmf.ui.viewers.tree.ITmfTreeViewerEntry; +import org.eclipse.linuxtools.tmf.ui.viewers.tree.TmfTreeColumnData; +import org.eclipse.linuxtools.tmf.ui.viewers.tree.TmfTreeColumnData.ITmfColumnPercentageProvider; +import org.eclipse.linuxtools.tmf.ui.viewers.tree.TmfTreeViewerEntry; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Composite; + +/** + * Tree viewer to display CPU usage information in a specified time range. It + * shows the process's TID, its name, the time spent on the CPU during that + * range, in % and absolute value. + * + * @author Geneviève Bastien + */ +public class CpuUsageComposite extends AbstractTmfTreeViewer { + + private LttngKernelCpuUsageAnalysis fModule = null; + + private static final String[] COLUMN_NAMES = new String[] { + Messages.CpuUsageComposite_ColumnTID, + Messages.CpuUsageComposite_ColumnProcess, + Messages.CpuUsageComposite_ColumnPercent, + Messages.CpuUsageComposite_ColumnTime + }; + + /* A map that saves the mapping of a thread ID to its executable name */ + private final Map fProcessNameMap = new HashMap<>(); + + /** Provides label for the CPU usage tree viewer cells */ + protected static class CpuLabelProvider extends TreeLabelProvider { + + @Override + public String getColumnText(Object element, int columnIndex) { + CpuUsageEntry obj = (CpuUsageEntry) element; + if (columnIndex == 0) { + return obj.getTid(); + } else if (columnIndex == 1) { + return obj.getProcessName(); + } else if (columnIndex == 2) { + return String.format(Messages.CpuUsageComposite_TextPercent, obj.getPercent()); + } else if (columnIndex == 3) { + return NLS.bind(Messages.CpuUsageComposite_TextTime, obj.getTime()); + } + + return element.toString(); + } + + } + + /** + * Constructor + * + * @param parent + * The parent composite that holds this viewer + */ + public CpuUsageComposite(Composite parent) { + super(parent, false); + setLabelProvider(new CpuLabelProvider()); + } + + @Override + protected ITmfTreeColumnDataProvider getColumnDataProvider() { + return new ITmfTreeColumnDataProvider() { + + @Override + public List getColumnData() { + /* All columns are sortable */ + List columns = new ArrayList<>(); + TmfTreeColumnData column = new TmfTreeColumnData(COLUMN_NAMES[0]); + column.setComparator(new ViewerComparator() { + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + CpuUsageEntry n1 = (CpuUsageEntry) e1; + CpuUsageEntry n2 = (CpuUsageEntry) e2; + + return n1.getTid().compareTo(n2.getTid()); + + } + }); + columns.add(column); + column = new TmfTreeColumnData(COLUMN_NAMES[1]); + column.setComparator(new ViewerComparator() { + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + CpuUsageEntry n1 = (CpuUsageEntry) e1; + CpuUsageEntry n2 = (CpuUsageEntry) e2; + + return n1.getProcessName().compareTo(n2.getProcessName()); + + } + }); + columns.add(column); + column = new TmfTreeColumnData(COLUMN_NAMES[2]); + column.setComparator(new ViewerComparator() { + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + CpuUsageEntry n1 = (CpuUsageEntry) e1; + CpuUsageEntry n2 = (CpuUsageEntry) e2; + + return n1.getPercent().compareTo(n2.getPercent()); + + } + }); + column.setPercentageProvider(new ITmfColumnPercentageProvider() { + + @Override + public double getPercentage(Object data) { + CpuUsageEntry parent = (CpuUsageEntry) data; + return parent.getPercent() / 100; + } + }); + columns.add(column); + column = new TmfTreeColumnData(COLUMN_NAMES[3]); + column.setComparator(new ViewerComparator() { + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + CpuUsageEntry n1 = (CpuUsageEntry) e1; + CpuUsageEntry n2 = (CpuUsageEntry) e2; + + return n1.getTime().compareTo(n2.getTime()); + + } + }); + columns.add(column); + + return columns; + } + + }; + } + + // ------------------------------------------------------------------------ + // Operations + // ------------------------------------------------------------------------ + + @Override + public void initializeDataSource() { + fModule = getTrace().getAnalysisModuleOfClass(LttngKernelCpuUsageAnalysis.class, LttngKernelCpuUsageAnalysis.ID); + if (fModule == null) { + return; + } + fModule.schedule(); + fModule.waitForInitialization(); + fProcessNameMap.clear(); + } + + @Override + protected ITmfTreeViewerEntry updateElements(long start, long end, boolean isSelection) { + if (isSelection || (start == end)) { + return null; + } + if (getTrace() == null || fModule == null) { + return null; + } + ITmfStateSystem ss = fModule.getStateSystem(); + /* Don't wait for the module completion, when it's ready, we'll know */ + if (ss == null) { + return null; + } + + /* Initialize the data */ + Map cpuUsageMap = fModule.getCpuUsageInRange(Math.max(start, getStartTime()), Math.min(end, getEndTime())); + + TmfTreeViewerEntry root = new TmfTreeViewerEntry(""); //$NON-NLS-1$ + List entryList = root.getChildren(); + + for (Entry entry : cpuUsageMap.entrySet()) { + /* + * Process only entries representing the total of all CPUs and that + * have time on CPU + */ + if (entry.getValue() == 0) { + continue; + } + if (!entry.getKey().startsWith(LttngKernelCpuUsageAnalysis.TOTAL)) { + continue; + } + String[] strings = entry.getKey().split(LttngKernelCpuUsageAnalysis.SPLIT_STRING, 2); + + if ((strings.length > 1) && !(strings[1].equals(LttngKernelCpuUsageAnalysis.TID_ZERO))) { + CpuUsageEntry obj = new CpuUsageEntry(strings[1], getProcessName(strings[1]), (double) entry.getValue() / (double) (end - start) * 100, entry.getValue()); + entryList.add(obj); + } + } + + return root; + } + + /* + * Get the process name from its TID by using the LTTng kernel analysis + * module + */ + private String getProcessName(String tid) { + String execName = fProcessNameMap.get(tid); + if (execName != null) { + return execName; + } + TmfStateSystemAnalysisModule module = getTrace().getAnalysisModuleOfClass(TmfStateSystemAnalysisModule.class, LttngKernelAnalysisModule.ID); + if (module == null) { + return tid; + } + /* + * Do not schedule the analysis here. It should have been executed when + * the CPU usage analysis was executed. If it's not available, there + * might be a good reason (disk space?) so don't force it. + */ + ITmfStateSystem kernelSs = module.getStateSystem(); + if (kernelSs == null) { + return tid; + } + + try { + int cpusNode = kernelSs.getQuarkAbsolute(Attributes.THREADS); + + /* Get the quarks for each cpu */ + List cpuNodes = kernelSs.getSubAttributes(cpusNode, false); + + for (Integer tidQuark : cpuNodes) { + if (kernelSs.getAttributeName(tidQuark).equals(tid)) { + int execNameQuark; + List execNameIntervals; + try { + execNameQuark = kernelSs.getQuarkRelative(tidQuark, Attributes.EXEC_NAME); + execNameIntervals = kernelSs.queryHistoryRange(execNameQuark, getStartTime(), getEndTime()); + } catch (AttributeNotFoundException e) { + /* No information on this thread (yet?), skip it for now */ + continue; + } catch (StateSystemDisposedException e) { + /* State system is closing down, no point continuing */ + break; + } + + for (ITmfStateInterval execNameInterval : execNameIntervals) { + if (!execNameInterval.getStateValue().isNull() && + execNameInterval.getStateValue().getType() == ITmfStateValue.Type.STRING) { + execName = execNameInterval.getStateValue().unboxStr(); + fProcessNameMap.put(tid, execName); + return execName; + } + } + } + } + + } catch (AttributeNotFoundException e) { + /* can't find the process name, just return the tid instead */ + } + return tid; + } + +} diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageEntry.java b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageEntry.java new file mode 100644 index 0000000000..79bb0ce26e --- /dev/null +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageEntry.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2014 É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: + * Geneviève Bastien - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.cpuusage; + +import org.eclipse.linuxtools.tmf.ui.viewers.tree.TmfTreeViewerEntry; + +/** + * Represents an entry in the tree viewer of the CPU usage view. An entry is a + * thread that occupied part of the CPU in the selected time range. + * + * @author Geneviève Bastien + */ +public class CpuUsageEntry extends TmfTreeViewerEntry { + private final String fTid; + private final String fProcessName; + private final Double fPercent; + private final Long fTime; + + /** + * Constructor + * + * @param tid + * The TID of the process + * @param name + * The thread's name + * @param percent + * The percentage CPU usage + * @param time + * The total amount of time spent on CPU + */ + public CpuUsageEntry(String tid, String name, double percent, long time) { + super(tid); + fTid = tid; + fProcessName = name; + fPercent = percent; + fTime = time; + } + + /** + * Get the TID of the thread represented by this entry + * + * @return The thread's TID + */ + public String getTid() { + return fTid; + } + + /** + * Get the process name + * + * @return The process name + */ + public String getProcessName() { + return fProcessName; + } + + /** + * Get the percentage of time spent on CPU in the time interval represented + * by this entry. + * + * @return The percentage of time spent on CPU + */ + public Double getPercent() { + return fPercent; + } + + /** + * Get the total time spent on CPU in the time interval represented by this + * entry. + * + * @return The total time spent on CPU + */ + public Long getTime() { + return fTime; + } +} diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageView.java b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageView.java new file mode 100644 index 0000000000..f69648e9c6 --- /dev/null +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageView.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2014 É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: + * Geneviève Bastien - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.cpuusage; + +import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace; +import org.eclipse.linuxtools.tmf.ui.viewers.xycharts.TmfXYChartViewer; +import org.eclipse.linuxtools.tmf.ui.views.TmfView; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; + +/** + * CPU usage view. It contains 2 viewers: one tree viewer showing all the + * threads who were on the CPU in the time range, and one XY chart viewer + * plotting the total time spent on CPU and the time of the threads selected in + * the tree viewer. + * + * @author Geneviève Bastien + */ +public class CpuUsageView extends TmfView { + + /** ID string */ + public static final String ID = "org.eclipse.linuxtools.lttng2.kernel.ui.views.cpuusage"; //$NON-NLS-1$ + + /** + * Constructor + */ + public CpuUsageView() { + super(Messages.CpuUsageView_Title); + } + + @Override + public void createPartControl(Composite parent) { + + final SashForm sash = new SashForm(parent, SWT.NONE); + + CpuUsageComposite treeViewer = new CpuUsageComposite(sash); + + /* Build the XY chart part of the view */ + TmfXYChartViewer xyViewer = new CpuUsageXYViewer(sash, treeViewer); + + sash.setLayout(new FillLayout()); + + /* Initialize the viewers with the currently selected trace */ + ITmfTrace trace = getActiveTrace(); + if (trace != null) { + TmfTraceSelectedSignal signal = new TmfTraceSelectedSignal(this, trace); + treeViewer.traceSelected(signal); + xyViewer.traceSelected(signal); + } + + } + + @Override + public void setFocus() { + } + +} diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageXYViewer.java b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageXYViewer.java new file mode 100644 index 0000000000..a87c5f6edc --- /dev/null +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/CpuUsageXYViewer.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2014 É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: + * Geneviève Bastien - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.cpuusage; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.linuxtools.internal.lttng2.kernel.ui.Activator; +import org.eclipse.linuxtools.lttng2.kernel.core.cpuusage.LttngKernelCpuUsageAnalysis; +import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException; +import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem; +import org.eclipse.linuxtools.tmf.ui.viewers.xycharts.linecharts.TmfCommonXLineChartViewer; +import org.eclipse.swt.widgets.Composite; + +/** + * CPU usage viewer with XY line chart. It displays the total CPU usage and that + * of the threads selected in the CPU usage tree viewer. + * + * @author Geneviève Bastien + */ +public class CpuUsageXYViewer extends TmfCommonXLineChartViewer { + + private LttngKernelCpuUsageAnalysis fModule = null; + + /* Maps a thread ID to a list of y values */ + private final Map fYValues = new LinkedHashMap<>(); + /* + * To avoid up and downs CPU usage when process is in and out of CPU + * frequently, use a smaller resolution to get better averages. + */ + private static final double RESOLUTION = 0.4; + + private long fSelectedThread = -1; + + /** + * Constructor + * + * @param parent + * parent composite + * @param treeViewer + * The tree viewer containing the list of threads with CPU usage. + * A listener will be added to that viewer so it can synchronize + * with the selection from the viewer. + */ + public CpuUsageXYViewer(Composite parent, CpuUsageComposite treeViewer) { + super(parent, Messages.CpuUsageXYViewer_Title, Messages.CpuUsageXYViewer_TimeXAxis, Messages.CpuUsageXYViewer_CpuYAxis); + setResolution(RESOLUTION); + + /* Add selection listener to tree viewer */ + treeViewer.addSelectionChangeListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + if (event.getSelection() instanceof IStructuredSelection) { + Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (selection instanceof CpuUsageEntry) { + CpuUsageEntry entry = (CpuUsageEntry) selection; + setSelectedThread(Long.valueOf(entry.getTid())); + } + } + } + }); + } + + @Override + protected void initializeDataSource() { + if (getTrace() != null) { + fModule = getTrace().getAnalysisModuleOfClass(LttngKernelCpuUsageAnalysis.class, LttngKernelCpuUsageAnalysis.ID); + if (fModule == null) { + return; + } + fModule.schedule(); + } + } + + private static double[] zeroFill(int nb) { + double[] arr = new double[nb]; + Arrays.fill(arr, 0.0); + return arr; + } + + @Override + protected void updateData(long start, long end, int nb, IProgressMonitor monitor) { + try { + if (getTrace() == null || fModule == null) { + return; + } + ITmfStateSystem ss = fModule.getStateSystem(); + /* + * Don't wait for the module completion or initialization, when it's + * ready, we'll know + */ + if (ss == null) { + return; + } + double[] xvalues = getXAxis(start, end, nb); + if (xvalues.length == 0) { + return; + } + setXAxis(xvalues); + + long traceStart = getStartTime(); + long traceEnd = getEndTime(); + long offset = getTimeOffset(); + long selectedThread = fSelectedThread; + + /* Initialize the data */ + Map cpuUsageMap = fModule.getCpuUsageInRange(Math.max(start, traceStart), Math.min(end, traceEnd)); + Map totalEntries = new HashMap<>(); + fYValues.clear(); + fYValues.put(Messages.CpuUsageXYViewer_Total, zeroFill(xvalues.length)); + String stringSelectedThread = Long.toString(selectedThread); + if (selectedThread != -1) { + fYValues.put(stringSelectedThread, zeroFill(xvalues.length)); + } + + for (Entry entry : cpuUsageMap.entrySet()) { + /* + * Process only entries representing the total of all CPUs and + * that have time on CPU + */ + if (entry.getValue() == 0) { + continue; + } + if (!entry.getKey().startsWith(LttngKernelCpuUsageAnalysis.TOTAL)) { + continue; + } + String[] strings = entry.getKey().split(LttngKernelCpuUsageAnalysis.SPLIT_STRING, 2); + + if ((strings.length > 1) && !(strings[1].equals(LttngKernelCpuUsageAnalysis.TID_ZERO))) { + /* This is the total cpu usage for a thread */ + totalEntries.put(strings[1], entry.getKey()); + } + } + + double prevX = xvalues[0]; + long prevTime = (long) prevX + offset; + /* + * make sure that time is in the trace range after double to long + * conversion + */ + prevTime = Math.max(traceStart, prevTime); + prevTime = Math.min(traceEnd, prevTime); + /* Get CPU usage statistics for each x value */ + for (int i = 1; i < xvalues.length; i++) { + if (monitor.isCanceled()) { + return; + } + long totalCpu = 0; + double x = xvalues[i]; + long time = (long) x + offset; + time = Math.max(traceStart, time); + time = Math.min(traceEnd, time); + + cpuUsageMap = fModule.getCpuUsageInRange(prevTime, time); + + /* + * Calculate the sum of all total entries, and add a data point + * to the selected one + */ + for (Entry entry : totalEntries.entrySet()) { + Long cpuEntry = cpuUsageMap.get(entry.getValue()); + cpuEntry = cpuEntry != null ? cpuEntry : 0L; + + totalCpu += cpuEntry; + + if (entry.getKey().equals(stringSelectedThread)) { + /* This is the total cpu usage for a thread */ + fYValues.get(entry.getKey())[i] = (double) cpuEntry / (double) (time - prevTime) * 100; + } + + } + fYValues.get(Messages.CpuUsageXYViewer_Total)[i] = (double) totalCpu / (double) (time - prevTime) * 100; + prevTime = time; + } + for (Entry entry : fYValues.entrySet()) { + setSeries(entry.getKey(), entry.getValue()); + } + if (monitor.isCanceled()) { + return; + } + updateDisplay(); + } catch (StateValueTypeException e) { + Activator.getDefault().logError("Error updating the data of the CPU usage view", e); //$NON-NLS-1$ + } + + } + + /** + * Set the selected thread ID, which will be graphed in this viewer + * + * @param tid + * The selected thread ID + */ + public void setSelectedThread(long tid) { + cancelUpdate(); + deleteSeries(Long.toString(fSelectedThread)); + fSelectedThread = tid; + updateContent(); + } + +} diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/Messages.java b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/Messages.java new file mode 100644 index 0000000000..6b70d8cd28 --- /dev/null +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/Messages.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2014 É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: + * Geneviève Bastien - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.cpuusage; + +import org.eclipse.osgi.util.NLS; + +/** + * Messages used in the LTTng kernel CPU usage view and viewers. + * + * @author Geneviève Bastien + */ +@SuppressWarnings("javadoc") +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.linuxtools.internal.lttng2.kernel.ui.views.cpuusage.messages"; //$NON-NLS-1$ + public static String CpuUsageComposite_ColumnPercent; + public static String CpuUsageComposite_ColumnProcess; + public static String CpuUsageComposite_ColumnTID; + public static String CpuUsageComposite_ColumnTime; + public static String CpuUsageComposite_TextPercent; + public static String CpuUsageComposite_TextTime; + public static String CpuUsageView_Title; + public static String CpuUsageXYViewer_CpuYAxis; + public static String CpuUsageXYViewer_TimeXAxis; + public static String CpuUsageXYViewer_Title; + public static String CpuUsageXYViewer_Total; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/messages.properties b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/messages.properties new file mode 100644 index 0000000000..bca6d6ab3c --- /dev/null +++ b/org.eclipse.linuxtools.lttng2.kernel.ui/src/org/eclipse/linuxtools/internal/lttng2/kernel/ui/views/cpuusage/messages.properties @@ -0,0 +1,23 @@ +############################################################################### +# Copyright (c) 2014 É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: +# Geneviève Bastien - Initial API and implementation +############################################################################### + +CpuUsageComposite_ColumnPercent=% +CpuUsageComposite_ColumnProcess=Process +CpuUsageComposite_ColumnTID=TID +CpuUsageComposite_ColumnTime=Time +CpuUsageComposite_TextPercent=%1$.3f %% +CpuUsageComposite_TextTime={0} ns +CpuUsageView_Title=CPU Usage +CpuUsageXYViewer_CpuYAxis=% CPU +CpuUsageXYViewer_TimeXAxis=Time +CpuUsageXYViewer_Title=CPU usage +CpuUsageXYViewer_Total=Total -- 2.34.1