/******************************************************************************* * Copyright (c) 2013 Red Hat Inc. * 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: * Red Hat Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.internal.perf; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.linuxtools.internal.perf.handlers.Messages; import org.eclipse.linuxtools.internal.perf.model.PMStatEntry; import org.eclipse.linuxtools.internal.perf.model.PMStatEntry.Type; import org.eclipse.linuxtools.profiling.launch.IRemoteFileProxy; import org.eclipse.linuxtools.profiling.launch.RemoteProxyManager; /** * Class containing all functionality for comparting perf statistics data. */ public class StatComparisonData extends BaseDataManipulator implements IPerfData { // Old stats file. private IPath oldFile; // New stats file. private IPath newFile; // Comparison result string. private String result = ""; //$NON-NLS-1$ // Title for this comparison run. private String title; // Unique data identifier. private String dataID; public StatComparisonData(String title, IPath oldFile, IPath newFile) { this.title = title; this.oldFile = oldFile; this.newFile = newFile; this.dataID = String.valueOf(((new Date().getTime()))); } @Override public String getPerfData() { return result; } @Override public String getTitle() { return title; } /** * Get unique identifier for this data object. * * @return String unique identifier based on this object's creation time. */ public String getDataID(){ return dataID; } /** * Generate a unique identifier based on the given file. The generation is a * simple concatenation between the file path and the time of this object's * creation. * * @param file File to generate uniqure id from. * @return String unique id for specified file. */ private String generateFileID(IPath file) { return file.toOSString() + dataID; } /** * Get path to old perf data file. * * @return String path corresponding to old perf data. */ public String getOldDataPath() { return oldFile.toPortableString(); } /** * Get path to new perf data file. * * @return String path corresponding to new perf data. */ public String getNewDataPath() { return newFile.toOSString(); } /** * Get a unique to for the old perf data file. * * @return String unique id. */ public String getOldDataID() { return generateFileID(oldFile); } /** * Get a unique to for the old perf data file. * * @return String unique id. */ public String getNewDataID() { return generateFileID(newFile); } /** * Compare stat data files and store the result in the result field. */ public void runComparison() { ArrayList<PMStatEntry> statsDiff = getComparisonStats(); if (!statsDiff.isEmpty()) { String[][] statsDiffStr = new String[statsDiff.size()][]; int currentRow = 0; // gather comparison results in a string array for (PMStatEntry statEntry : statsDiff) { statsDiffStr[currentRow] = statEntry.toStringArray(); currentRow++; } // apply format to each entry and set the result String format = getFormat(statsDiffStr); String curLine; for (String[] statEntry : statsDiffStr) { curLine = String.format(format, (Object[]) statEntry); curLine = curLine.contains(PMStatEntry.TIME) ? "\n" + curLine //$NON-NLS-1$ : curLine; result += curLine; } } else{ } } /** * Return a PMStatEntry array with the result of the comparison between the * old and new stat data files * @return */ public ArrayList<PMStatEntry> getComparisonStats() { ArrayList<PMStatEntry> oldStats = collectStats(oldFile); ArrayList<PMStatEntry> newStats = collectStats(newFile); ArrayList<PMStatEntry> result = new ArrayList<>(); for (PMStatEntry oldEntry : oldStats) { for (PMStatEntry newEntry : newStats) { if (oldEntry.equalEvents(newEntry)) { result.add(oldEntry.compare(newEntry)); continue; } } } return result; } /** * Collect statistics entries from the specified stat data file. * * @param file file to collect from * @return List containing statistics entries from the given file. */ private static ArrayList<PMStatEntry> collectStats(IPath file) { ArrayList<PMStatEntry> result = new ArrayList<>(); BufferedReader statReader = null; URI fileURI = null; try { fileURI = new URI(file.toPortableString()); IRemoteFileProxy proxy = null; proxy = RemoteProxyManager.getInstance().getFileProxy(fileURI); IFileStore newDataFileStore = proxy.getResource(fileURI.getPath()); statReader = new BufferedReader(new InputStreamReader(newDataFileStore.openInputStream(EFS.NONE, null))); // pattern for a valid perf stat entry Pattern entryPattern = Pattern.compile(PMStatEntry.getString(Type.ENTRY_PATTERN)); // pattern for last stat entry (seconds elapsed): Pattern totalTimePattern = Pattern.compile(PMStatEntry.getString(Type.TIME_PATTERN)); String line; while((line = statReader.readLine()) != null ){ line = line.trim(); Matcher match = entryPattern.matcher(line); String samples, event, usage, units, delta, scale; PMStatEntry statEntry; if(match.find()){ // extract information from groups samples = match.group(1); event = match.group(2); usage = match.group(7); units = match.group(8); delta = match.group(10); scale = match.group(14); // create stat entry statEntry = new PMStatEntry(toFloat(samples), event, toFloat(usage), units, toFloat(delta), toFloat(scale)); // add stat entry to results list result.add(statEntry); } else if(line.contains(PMStatEntry.TIME)){ // match seconds elapsed pattern match = totalTimePattern.matcher(line); if(match.find()){ samples = match.group(1); event = match.group(2); delta = match.group(4); // create stat entry statEntry = new PMStatEntry(toFloat(samples), event, 0, null, toFloat(delta), 0); result.add(statEntry); } } } return result; } catch (IOException|CoreException|URISyntaxException e) { PerfPlugin.getDefault().openError(e, Messages.MsgError); } finally { try { if (statReader != null) { statReader.close(); } } catch (IOException e) { PerfPlugin.getDefault().openError(e, Messages.PerfResourceLeak_title); } } return result; } /** * Get formatting string from unformatted table. * * @param table array to construct formatting for. * @return Formatting string representing the proper way to format the given * table. */ private String getFormat(String[][] table) { // all entries have the same number of columns int[] maxCharLen = new int[table[0].length]; // collect max number of characters per column for (int i = 0; i < table.length; i++) { for (int j = 0; j < table[i].length; j++) { maxCharLen[j] = Math.max(maxCharLen[j], table[i][j].length()); } } // prepare format arguments ArrayList<Integer> arguments = new ArrayList<>(); for (int length : maxCharLen) { arguments.add(length); } // generate format string String entryFormat = String.format( PMStatEntry.getString(Type.ENTRY_FORMAT), arguments.toArray()); return entryFormat; } /** * Get float representation of specified string. * * @param str String convert * @return Float representation of string. */ public static float toFloat(String str) { try { // remove commas from number string representation return (str == null) ? 0 : Float.parseFloat(str.replace(",", "")); //$NON-NLS-1$//$NON-NLS-2$ } catch (NumberFormatException e) { return 0; } } }