/********************************************************************** * Copyright (c) 2016, 2017 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 static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.widgets.Composite; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelThreadInformationProvider; import org.eclipse.tracecompass.analysis.os.linux.core.kernelmemoryusage.KernelMemoryAnalysisModule; import org.eclipse.tracecompass.analysis.os.linux.core.kernelmemoryusage.KernelMemoryStateProvider; import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Activator; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; 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.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.tree.AbstractTmfTreeViewer; import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider; import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeViewerEntry; import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData; import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeViewerEntry; /** * Tree viewer to select which process to display in the kernel memory usage * chart. * * @author Mahdi Zolnouri * @author Wassim Nasrallah * @author Najib Arbaoui */ public class KernelMemoryUsageTreeViewer extends AbstractTmfTreeViewer { private KernelMemoryAnalysisModule fModule = null; private String fSelectedThread = null; private static final String[] COLUMN_NAMES = new String[] { Messages.KernelMemoryUsageComposite_ColumnTID, Messages.KernelMemoryUsageComposite_ColumnProcess }; /* A map that saves the mapping of a thread ID to its executable name */ private final Map<String, String> fProcessNameMap = new HashMap<>(); /** Provides label for the Kernel memory usage tree viewer cells */ protected static class KernelMemoryLabelProvider extends TreeLabelProvider { @Override public String getColumnText(Object element, int columnIndex) { KernelMemoryUsageEntry obj = (KernelMemoryUsageEntry) element; if (columnIndex == 0) { return obj.getTid(); } else if (columnIndex == 1) { return obj.getProcessName(); } return element.toString(); } } /** * Constructor * * @param parent * The parent composite that holds this viewer */ public KernelMemoryUsageTreeViewer(Composite parent) { super(parent, false); setLabelProvider(new KernelMemoryLabelProvider()); } @Override protected ITmfTreeColumnDataProvider getColumnDataProvider() { return new ITmfTreeColumnDataProvider() { @Override public List<TmfTreeColumnData> getColumnData() { /* All columns are sortable */ List<TmfTreeColumnData> columns = new ArrayList<>(); TmfTreeColumnData column = new TmfTreeColumnData(COLUMN_NAMES[0]); column.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { KernelMemoryUsageEntry n1 = (KernelMemoryUsageEntry) e1; KernelMemoryUsageEntry n2 = (KernelMemoryUsageEntry) 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) { KernelMemoryUsageEntry n1 = (KernelMemoryUsageEntry) e1; KernelMemoryUsageEntry n2 = (KernelMemoryUsageEntry) e2; return n1.getProcessName().compareTo(n2.getProcessName()); } }); columns.add(column); return columns; } }; } @Override protected ITmfTrace getTrace() { return super.getTrace(); } // ------------------------------------------------------------------------ // Operations // ------------------------------------------------------------------------ @Override protected void contentChanged(ITmfTreeViewerEntry rootEntry) { String selectedThread = fSelectedThread; if (selectedThread != null) { /* Find the selected thread among the inputs */ for (ITmfTreeViewerEntry entry : rootEntry.getChildren()) { if (entry instanceof KernelMemoryUsageEntry) { if (selectedThread.equals(((KernelMemoryUsageEntry) entry).getTid())) { List<ITmfTreeViewerEntry> list = Collections.singletonList(entry); super.setSelection(list); return; } } } } } @Override public void initializeDataSource() { /* Should not be called while trace is still null */ ITmfTrace trace = checkNotNull(getTrace()); fModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, KernelMemoryAnalysisModule.class, KernelMemoryAnalysisModule.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; } KernelMemoryAnalysisModule module = fModule; if (getTrace() == null || module == null) { return null; } module.waitForInitialization(); ITmfStateSystem ss = module.getStateSystem(); if (ss == null) { return null; } ss.waitUntilBuilt(); TmfTreeViewerEntry root = new TmfTreeViewerEntry(""); //$NON-NLS-1$ List<ITmfTreeViewerEntry> entryList = root.getChildren(); try { long newStart = Math.max(start, ss.getStartTime()); long newEnd = Math.min(end, ss.getCurrentEndTime()); if (ss.getStartTime() > newEnd || ss.getCurrentEndTime() < start) { return root; } List<ITmfStateInterval> memoryStates = ss.queryFullState(newStart); List<Integer> threadQuarkList = ss.getSubAttributes(ITmfStateSystem.ROOT_ATTRIBUTE, false); for (Integer threadQuark : threadQuarkList) { ITmfStateInterval threadMemoryInterval = memoryStates.get(threadQuark); if (threadMemoryInterval.getEndTime() < end) { String tid = ss.getAttributeName(threadQuark); String procname = getProcessName(tid); KernelMemoryUsageEntry obj = new KernelMemoryUsageEntry(tid, procname); entryList.add(obj); } } } catch (StateSystemDisposedException e) { Activator.getDefault().logError(e.getMessage(), e); } 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; } if (tid.equals(KernelMemoryStateProvider.OTHER_TID)) { fProcessNameMap.put(tid, tid); return tid; } ITmfTrace trace = checkNotNull(getTrace()); KernelAnalysisModule kernelModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, KernelAnalysisModule.class, KernelAnalysisModule.ID); if (kernelModule == null) { return tid; } execName = KernelThreadInformationProvider.getExecutableName(kernelModule, Integer.parseInt(tid)); if (execName == null) { return tid; } fProcessNameMap.put(tid, execName); return execName; } /** * Set the currently selected thread ID * * @param tid * The selected thread ID */ public void setSelectedThread(String tid) { fSelectedThread = tid; } @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(); final @Nullable Object data = ctx.getData(KernelMemoryUsageView.KERNEL_MEMORY); String thread = data instanceof String ? (String) data : null; setSelectedThread(thread); } }