/*******************************************************************************
* (C) Copyright 2010 IBM Corp. 2010
* 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:
* Thavidu Ranatunga (IBM) - Initial implementation.
*******************************************************************************/
package org.eclipse.linuxtools.internal.perf.tests;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.linuxtools.internal.perf.PerfCore;
import org.eclipse.linuxtools.internal.perf.PerfPlugin;
import org.eclipse.linuxtools.internal.perf.launch.PerfEventsTab;
import org.eclipse.linuxtools.internal.perf.launch.PerfOptionsTab;
import org.eclipse.linuxtools.internal.perf.model.PMCommand;
import org.eclipse.linuxtools.internal.perf.model.PMDso;
import org.eclipse.linuxtools.internal.perf.model.PMEvent;
import org.eclipse.linuxtools.internal.perf.model.PMFile;
import org.eclipse.linuxtools.internal.perf.model.PMSymbol;
import org.eclipse.linuxtools.internal.perf.model.TreeParent;
import org.eclipse.linuxtools.internal.perf.ui.PerfDoubleClickAction;
import org.eclipse.linuxtools.internal.perf.ui.PerfProfileView;
import org.eclipse.linuxtools.profiling.tests.AbstractTest;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.FrameworkUtil;
public class ModelTest extends AbstractTest {
private ILaunchConfiguration config;
private Stack<Class<?>> stack;
@Before
public void setUp() throws Exception {
proj = createProjectAndBuild(FrameworkUtil.getBundle(this.getClass()), "fibTest"); //$NON-NLS-1$
config = createConfiguration(proj.getProject());
Class<?>[] klassList = new Class<?>[] { PMSymbol.class, PMFile.class,
PMDso.class, PMCommand.class, PMEvent.class };
stack = new Stack<>();
Collections.addAll(stack, klassList);
}
@After
public void tearDown() throws Exception {
deleteProject(proj);
}
@Override
protected ILaunchConfigurationType getLaunchConfigType() {
return getLaunchManager().getLaunchConfigurationType(PerfPlugin.LAUNCHCONF_ID);
}
@Override
protected void setProfileAttributes(ILaunchConfigurationWorkingCopy wc) {
PerfEventsTab eventsTab = new PerfEventsTab();
PerfOptionsTab optionsTab = new PerfOptionsTab();
wc.setAttribute(PerfPlugin.ATTR_SourceLineNumbers, false);
eventsTab.setDefaults(wc);
optionsTab.setDefaults(wc);
}
@Test
public void testModelDefaultGenericStructure() {
TreeParent invisibleRoot = buildModel(
"resources/defaultevent-data/perf.data",
"resources/defaultevent-data/perf.data.txt",
"resources/defaultevent-data/perf.data.err.log");
checkChildrenStructure(invisibleRoot, stack);
}
@Test
public void testModelMultiEventGenericStructure() {
TreeParent invisibleRoot = buildModel(
"resources/multievent-data/perf.data",
"resources/multievent-data/perf.data.txt",
"resources/multievent-data/perf.data.err.log");
checkChildrenStructure(invisibleRoot, stack);
}
@Test
public void testPercentages() {
TreeParent invisibleRoot = buildModel(
"resources/defaultevent-data/perf.data",
"resources/defaultevent-data/perf.data.txt",
"resources/defaultevent-data/perf.data.err.log");
checkChildrenPercentages(invisibleRoot, invisibleRoot.getPercent());
}
@Test
public void testDoubleClickAction () {
TreeParent invisibleRoot = buildModel(
"resources/defaultevent-data/perf.data",
"resources/defaultevent-data/perf.data.txt",
"resources/defaultevent-data/perf.data.err.log");
PerfPlugin.getDefault().setModelRoot(invisibleRoot);
// update the model root for the view
PerfCore.refreshView("resources/defaultevent-data/perf.data");
// number of parents excluding invisibleRoot
int numOfParents = getNumberOfParents(invisibleRoot) - 1;
// create a double click action to act on the tree viewer
try {
PerfProfileView view = (PerfProfileView) PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getActivePage()
.showView(PerfPlugin.VIEW_ID);
TreeViewer tv = view.getTreeViewer();
PerfDoubleClickAction dblClick = new PerfDoubleClickAction(tv);
// double click every element
doubleClickAllChildren(invisibleRoot, tv, dblClick);
// If all elements are expanded, this is the number of elements
// in our model that have children.
assertEquals(numOfParents, tv.getExpandedElements().length);
} catch (PartInitException e) {
fail("Failed to open the Profiling View.");
}
}
@Test
public void testParserMultiEvent() {
TreeParent invisibleRoot = buildModel(
"resources/multievent-data/perf.data",
"resources/multievent-data/perf.data.txt",
"resources/multievent-data/perf.data.err.log");
assertEquals(invisibleRoot.getChildren().length, 5);
String cur = null;
for (TreeParent event : invisibleRoot.getChildren()) {
cur = event.getName();
// Assert specific properties extracted by the parser.
if ("cpu-clock".equals(cur)) {
assertTrue(event.hasChildren());
assertEquals(event.getChildren().length, 1);
TreeParent cmd = event.getChildren()[0];
assertEquals(cmd.getChildren().length, 1);
String[] cmdLabels = { "hellotest" };
checkCommadLabels(cmdLabels, cmd);
} else if ("task-clock".equals(cur)) {
assertTrue(event.hasChildren());
assertEquals(event.getChildren().length, 1);
TreeParent cmd = event.getChildren()[0];
assertEquals(cmd.getChildren().length, 1);
String[] cmdLabels = { "hellotest" };
checkCommadLabels(cmdLabels, cmd);
} else if ("page-faults".equals(cur)) {
assertTrue(event.hasChildren());
assertEquals(event.getChildren().length, 1);
TreeParent cmd = event.getChildren()[0];
assertEquals(cmd.getChildren().length, 3);
String[] cmdLabels = { "ld-2.14.90.so", "[kernel.kallsyms]",
"libc-2.14.90.so" };
checkCommadLabels(cmdLabels, cmd);
} else if ("minor-faults".equals(cur)) {
assertTrue(event.hasChildren());
assertEquals(event.getChildren().length, 1);
TreeParent cmd = event.getChildren()[0];
assertEquals(cmd.getChildren().length, 3);
String[] cmdLabels = { "ld-2.14.90.so", "[kernel.kallsyms]",
"libc-2.14.90.so" };
checkCommadLabels(cmdLabels, cmd);
} else if ("major-faults".equals(cur)) {
assertFalse(event.hasChildren());
}
}
}
@Test
public void testParserDefaultEvent() {
TreeParent invisibleRoot = buildModel(
"resources/defaultevent-data/perf.data",
"resources/defaultevent-data/perf.data.txt",
"resources/defaultevent-data/perf.data.err.log");
// Assert specific properties extracted by the parser.
assertEquals(invisibleRoot.getChildren().length, 1);
TreeParent event = invisibleRoot.getChildren()[0];
assertEquals(event.getName(), "cycles");
assertTrue(event.hasChildren());
assertEquals(event.getChildren().length, 1);
TreeParent cmd = event.getChildren()[0];
assertTrue(cmd.hasChildren());
assertEquals(cmd.getChildren().length, 4);
String[] cmdLabels = { "hellotest", "[kernel.kallsyms]",
"ld-2.14.90.so", "perf" };
checkCommadLabels(cmdLabels, cmd);
}
@Test
public void testParseEventList() throws FileNotFoundException {
BufferedReader input = new BufferedReader(new FileReader("resources/simple-perf-event-list"));
Map<String, List<String>> eventList = PerfCore.parseEventList(input);
for(String key : eventList.keySet()){
if ("Raw hardware event descriptor".equals(key)) {
assertTrue(eventList.get(key).contains("rNNN"));
assertTrue(eventList.get(key).contains("cpu/t1=v1"));
} else if ("Hardware breakpoint".equals(key)) {
assertTrue(eventList.get(key).contains("mem:<addr>"));
} else if ("Software event".equals(key)) {
assertTrue(eventList.get(key).contains("cpu-clock"));
assertTrue(eventList.get(key).contains("task-clock"));
} else if ("Hardware cache event".equals(key)) {
assertTrue(eventList.get(key).contains("L1-dcache-loads"));
assertTrue(eventList.get(key).contains("L1-dcache-load-misses"));
} else if ("Tracepoint event".equals(key)) {
assertTrue(eventList.get(key).contains("mac80211:drv_return_void"));
assertTrue(eventList.get(key).contains("mac80211:drv_return_int"));
} else if ("Hardware event".equals(key)) {
assertTrue(eventList.get(key).contains("cpu-cycles"));
assertTrue(eventList.get(key).contains("stalled-cycles-frontend"));
}
}
}
@Test
public void testParseAnnotation() throws FileNotFoundException {
BufferedReader input = new BufferedReader(new FileReader(
"resources/perf-annotation-data"));
// Set up arguments for the annotation parser.
IPath workingDir = Path.fromOSString("/working/directory/");
PMCommand cmd = new PMCommand("testCommand");
PMDso dso = new PMDso("testDso", false);
PMFile tmpFile = new PMFile(PerfPlugin.STRINGS_UnfiledSymbols);
PMSymbol sym = new PMSymbol("testSym", 0, 0);
// Set children and respective parents.
cmd.addChild(dso);
dso.addChild(tmpFile);
tmpFile.addChild(sym);
dso.setParent(cmd);
tmpFile.setParent(dso);
sym.setParent(tmpFile);
PerfCore.parseAnnotation(null, input, workingDir, dso, sym);
// Expected results data.
String expectedDsoPath = "/working/directory/fibonacci";
String expectedFilePath = "/home/user/workspace/fibonacci/Debug/../src/fibonacci.cpp";
assertTrue(expectedDsoPath.equals(dso.getPath()));
assertEquals(dso.getChildren().length, 2);
for (TreeParent dsoChild : dso.getChildren()) {
String filePath = ((PMFile) dsoChild).getPath();
if (PerfPlugin.STRINGS_UnfiledSymbols.equals(filePath)) {
assertFalse(dsoChild.hasChildren());
} else {
assertTrue(expectedFilePath.equals(filePath));
assertTrue(dsoChild.hasChildren());
assertEquals(dsoChild.getChildren().length, 1);
TreeParent curSym = dsoChild.getChildren()[0];
assertTrue(curSym.hasChildren());
assertEquals(curSym.getChildren().length, 5);
float percentCount = 0;
for (TreeParent symChild : curSym.getChildren()) {
percentCount += symChild.getPercent();
}
assertEquals(Math.ceil(percentCount), 100.0, 0.0);
}
}
}
@Test
public void testAnnotateString() throws CoreException {
ILaunchConfigurationWorkingCopy tempConfig = config.copy("test-config");
tempConfig
.setAttribute(PerfPlugin.ATTR_Kernel_Location, "/boot/kernel");
tempConfig.setAttribute(PerfPlugin.ATTR_ModuleSymbols, true);
String[] annotateString = PerfCore.getAnnotateString(tempConfig, "dso",
"symbol", "resources/defaultevent-data/perf.data", false);
String[] expectedString = new String[] { PerfPlugin.PERF_COMMAND,
"annotate", "--stdio", "-d", "dso", "-s", "symbol", "-l", "-P",
"--vmlinux", "/boot/kernel", "-m", "-i",
"resources/defaultevent-data/perf.data",
"<", "/dev/null" };
assertArrayEquals(expectedString, annotateString);
}
@Test
public void testRecordString() throws CoreException {
ILaunchConfigurationWorkingCopy tempConfig = config.copy("test-config");
tempConfig.setAttribute(PerfPlugin.ATTR_Record_Realtime, true);
tempConfig.setAttribute(PerfPlugin.ATTR_Record_Realtime_Priority, 2);
tempConfig.setAttribute(PerfPlugin.ATTR_Record_Verbose, true);
tempConfig.setAttribute(PerfPlugin.ATTR_Multiplex, true);
ArrayList<String> selectedEvents = new ArrayList<>();
selectedEvents.add("cpu-cycles");
selectedEvents.add("cache-misses");
selectedEvents.add("cpu-clock");
tempConfig.setAttribute(PerfPlugin.ATTR_SelectedEvents, selectedEvents);
tempConfig.setAttribute(PerfPlugin.ATTR_DefaultEvent, false);
String[] recordString = PerfCore.getRecordString(tempConfig);
assertNotNull(recordString);
String[] expectedString = { PerfPlugin.PERF_COMMAND, "record",
"-r", "2", "-v", "-M", "-e", "cpu-cycles", "-e", "cache-misses",
"-e", "cpu-clock" };
assertArrayEquals(expectedString, recordString);
}
@Test
public void testReportString() throws CoreException {
ILaunchConfigurationWorkingCopy tempConfig = null;
tempConfig = config.copy("test-config");
tempConfig
.setAttribute(PerfPlugin.ATTR_Kernel_Location, "/boot/kernel");
tempConfig.setAttribute(PerfPlugin.ATTR_ModuleSymbols, true);
String[] reportString = PerfCore.getReportString(tempConfig,
"resources/defaultevent-data/perf.data");
assertNotNull(reportString);
String[] expectedString = { PerfPlugin.PERF_COMMAND, "report",
"--sort", "comm,dso,sym", "-n", "-t", "" + (char) 1,
"--vmlinux", "/boot/kernel", "-m", "-i",
"resources/defaultevent-data/perf.data" };
assertArrayEquals(expectedString, reportString);
}
/**
* @param root some element that will serve as the root
* @param sum the expected sum of the percentages of this root's
* immediate children
*/
private void checkChildrenPercentages (TreeParent root, float sum) {
float actualSum = 0;
// If a root has no children we're done
if (root.getChildren().length != 0) {
for (TreeParent child : root.getChildren()) {
actualSum += child.getPercent();
checkChildrenPercentages(child, child.getPercent());
}
// some top-level elements have an undefined percentage but
// their children have defined percentages
// eg. the invisible root, and PMCommand
if (actualSum != 100 && sum != -1){
assertTrue(actualSum/sum <= 1.0 && actualSum/sum >= 0.99);
}
}
}
/**
* @param root some element that will serve as the root
* @param stack a stack of classes
*/
private void checkChildrenStructure (TreeParent root, Stack<Class<?>> stack){
if (!stack.isEmpty()){
// children of root must be instances of the top class on the stack
Class<?> klass = stack.pop();
for (TreeParent tp : root.getChildren()){
// tp.getClass() instanceof klass
assertTrue(klass.isAssignableFrom(tp.getClass()));
// each sibling needs its own stack
Stack<Class<?>> newStack = new Stack<>();
newStack.addAll(stack);
checkChildrenStructure(tp, newStack);
}
}
}
/**
* Performs a Perf double-click action on every element in the
* TreeViewer model.
*
* @param root some element that will serve as the root
* @param tv a TreeViewer containing elements from the Perf model
* @param dblClick the double-click action to perform on every
* element of the TreeViewer.
*/
private void doubleClickAllChildren(TreeParent root, TreeViewer tv,
PerfDoubleClickAction dblClick) {
for (TreeParent child : root.getChildren()) {
// see PerfDoubleClickAction for IStructuredSelection
tv.setSelection(new StructuredSelection(child));
dblClick.run();
doubleClickAllChildren(child, tv, dblClick);
}
}
/**
* Find the number of ancestors of the given root that have children.
* This includes the given root in the computation.
*
* @param root some element that will serve as the root
* @return the number of elements under, and including the
* given root, that have children elements.
*/
private int getNumberOfParents(TreeParent root) {
int ret = root.hasChildren() ? 1 : 0;
for (TreeParent child : root.getChildren()) {
ret += getNumberOfParents(child);
}
return ret;
}
/**
* Build model based on perf data file report.
* @param perfDataLoc location of perf data file
* @param perfTextDataLoc location of perf data text file
* @param perfErrorDataLoc location of error log file
* @return tree model based on perf data report.
*/
private TreeParent buildModel(String perfDataLoc, String perfTextDataLoc,
String perfErrorDataLoc) {
TreeParent invisibleRoot = new TreeParent("");
BufferedReader input = null;
BufferedReader error = null;
try {
input = new BufferedReader(new FileReader(perfTextDataLoc));
error = new BufferedReader(new FileReader(perfErrorDataLoc));
} catch (IOException e) {
e.printStackTrace();
fail(e.getMessage());
}
PerfCore.parseReport(config, null, null, perfDataLoc, null,
invisibleRoot, false, input, error);
return invisibleRoot;
}
/**
* Check whether the command labels in model rooted at cmd exist in
* list of labels cmdLabels.
* @param cmdLabels list of command labels
* @param cmd root of tree model
*/
private void checkCommadLabels(String[] cmdLabels, TreeParent cmd) {
List<String> cmdList = new ArrayList<>(Arrays.asList(cmdLabels));
for (TreeParent dso : cmd.getChildren()) {
assertTrue(cmdList.get(0).equals(dso.getName()));
cmdList.remove(0);
}
}
}