/**********************************************************************
* 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 v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
**********************************************************************/
package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.kernelmemoryusage;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tracecompass.analysis.os.linux.core.kernelmemoryusage.KernelMemoryAnalysisModule;
import org.eclipse.tracecompass.common.core.format.DataSizeWithUnitFormat;
import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Activator;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfCommonXLineChartViewer;
import org.swtchart.Chart;
/**
* Memory usage view
*
* @author Samuel Gagnon
* @author Wassim Nasrallah
*/
public class KernelMemoryUsageViewer extends TmfCommonXLineChartViewer {
private static final String NOT_SELECTED = "-1"; //$NON-NLS-1$
private TmfStateSystemAnalysisModule fModule = null;
private String fSelectedThread = NOT_SELECTED;
/**
* Constructor
*
* @param parent
* parent view
*/
public KernelMemoryUsageViewer(Composite parent) {
super(parent, Messages.MemoryUsageViewer_title, Messages.MemoryUsageViewer_xAxis, Messages.MemoryUsageViewer_yAxis);
Chart chart = getSwtChart();
chart.getAxisSet().getYAxis(0).getTick().setFormat(DataSizeWithUnitFormat.getInstance());
chart.getLegend().setPosition(SWT.BOTTOM);
chart.getLegend().setVisible(false);
}
@Override
protected void initializeDataSource() {
ITmfTrace trace = getTrace();
if (trace != null) {
fModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, TmfStateSystemAnalysisModule.class, KernelMemoryAnalysisModule.ID);
if (fModule == null) {
return;
}
fModule.schedule();
}
}
@Override
protected void updateData(long start, long end, int nb, IProgressMonitor monitor) {
TmfStateSystemAnalysisModule module = fModule;
if (getTrace() == null || module == null) {
return;
}
if (!module.waitForInitialization()) {
return;
}
ITmfStateSystem ss = module.getStateSystem();
if (ss == null) {
throw new IllegalStateException("No state system for the module " + module.toString()); //$NON-NLS-1$
}
double[] xvalues = getXAxis(start, end, nb);
if (xvalues.length == 0) {
return;
}
long clampedEnd = Math.min(end, ss.getCurrentEndTime());
if (clampedEnd < ss.getStartTime()) {
return;
}
setXAxis(xvalues);
try {
/**
* For a given time range, we plot two lines representing the memory
* allocation. The first line represent the total memory allocation
* of every process. The second line represent the memory allocation
* of the selected thread.
*/
double[] totalKernelMemoryValues = new double[xvalues.length];
double[] selectedThreadValues = new double[xvalues.length];
for (int i = 0; i < xvalues.length; i++) {
if (monitor.isCanceled()) {
return;
}
double x = xvalues[i];
long t = (long) x + getTimeOffset();
if( ss.getCurrentEndTime() < t || ss.getStartTime() > t) {
selectedThreadValues[i] = 0;
continue;
}
List<ITmfStateInterval> kernelState = ss.queryFullState(t);
/* The subattributes of the root are the different threads */
List<Integer> threadQuarkList = ss.getSubAttributes(-1, false);
/* We add the value of each thread to the total quantity */
for (Integer threadQuark : threadQuarkList) {
ITmfStateInterval threadMemoryInterval = kernelState.get(threadQuark);
long value = threadMemoryInterval.getStateValue().unboxLong();
totalKernelMemoryValues[i] += value;
String tid = ss.getAttributeName(threadQuark);
if (tid.equals(fSelectedThread)) {
selectedThreadValues[i] = value;
}
}
}
/**
* For each thread, we look for its lowest value since the beginning
* of the trace. This way, we can avoid negative values in the plot.
*/
double totalKernelMemoryValuesShift = 0;
double selectThreadValuesShift = 0;
/*
* The lowest value we are searching is at the end of the current
* selected zone
*/
List<ITmfStateInterval> kernelState = ss.queryFullState(clampedEnd);
List<Integer> threadQuarkList = ss.getSubAttributes(-1, false);
/* We add the lowest value of each thread */
for (Integer threadQuark : threadQuarkList) {
int lowestMemoryQuark = ss.getQuarkRelative(threadQuark, KernelMemoryAnalysisModule.THREAD_LOWEST_MEMORY_VALUE);
ITmfStateInterval lowestMemoryInterval = kernelState.get(lowestMemoryQuark);
long lowestMemoryValue = lowestMemoryInterval.getStateValue().unboxLong();
// We want to add up a positive quantity.
totalKernelMemoryValuesShift -= lowestMemoryValue;
String tid = ss.getAttributeName(threadQuark);
if (tid.equals(fSelectedThread)) {
// We want to add up a positive quantity.
selectThreadValuesShift = -lowestMemoryValue;
}
}
/**
* We shift the two displayed lines up.
*/
for (int i = 0; i < xvalues.length; i++) {
totalKernelMemoryValues[i] += totalKernelMemoryValuesShift;
selectedThreadValues[i] += selectThreadValuesShift;
}
setSeries(Messages.MemoryUsageViewer_Total, totalKernelMemoryValues);
if (fSelectedThread != NOT_SELECTED) {
setSeries(fSelectedThread, selectedThreadValues);
}
updateDisplay();
} catch (TimeRangeException | StateSystemDisposedException | AttributeNotFoundException e) {
Activator.getDefault().logError(e.getMessage(), e);
}
}
/**
* Set the selected thread ID, which will be graphed in this viewer
*
* @param tid
* The selected thread ID
*/
public void setSelectedThread(String tid) {
cancelUpdate();
deleteSeries(fSelectedThread);
fSelectedThread = tid;
updateContent();
}
@Override
@TmfSignalHandler
public void traceSelected(TmfTraceSelectedSignal signal) {
initSelection();
super.traceSelected(signal);
}
@Override
@TmfSignalHandler
public void traceOpened(TmfTraceOpenedSignal signal) {
initSelection();
super.traceOpened(signal);
}
private void initSelection() {
TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
Object data = ctx.getData(KernelMemoryUsageView.KERNEL_MEMORY);
String thread = data instanceof String ? (String) data : NOT_SELECTED;
setSelectedThread(thread);
}
}