| /******************************************************************************* |
| * Copyright (c) 2009, 2016 Xored Software Inc and others. |
| * 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: |
| * Xored Software Inc - initial API and implementation and/or initial documentation |
| *******************************************************************************/ |
| package org.eclipse.rcptt.reporting.util; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.rcptt.ecl.core.EclException; |
| import org.eclipse.rcptt.ecl.core.ProcessStatus; |
| import org.eclipse.rcptt.ecl.internal.core.ProcessStatusConverter; |
| import org.eclipse.rcptt.internal.core.RcpttPlugin; |
| import org.eclipse.rcptt.reporting.ItemKind; |
| import org.eclipse.rcptt.reporting.Q7Info; |
| import org.eclipse.rcptt.reporting.core.ImageEntry; |
| import org.eclipse.rcptt.reporting.core.ReportHelper; |
| import org.eclipse.rcptt.reporting.core.SimpleSeverity; |
| import org.eclipse.rcptt.reporting.core.TimeFormatHelper; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.EclipseStatus; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.JavaException; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.JavaStackTraceEntry; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Event; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.report.LoggingCategory; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Node; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Report; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Screenshot; |
| import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Snaphot; |
| import org.eclipse.rcptt.sherlock.core.reporting.ReportBuilder; |
| import org.eclipse.rcptt.sherlock.core.reporting.SimpleReportGenerator; |
| import org.eclipse.rcptt.tesla.core.TeslaFeatures; |
| import org.eclipse.rcptt.tesla.core.info.AdvancedInformation; |
| import org.eclipse.rcptt.tesla.core.info.Q7WaitInfo; |
| import org.eclipse.rcptt.tesla.core.info.Q7WaitInfoRoot; |
| import org.eclipse.rcptt.tesla.core.utils.AdvancedInformationGenerator; |
| import org.eclipse.rcptt.util.StringUtils; |
| |
| public class RcpttReportGenerator { |
| private final SimpleReportGenerator simpleReportGenerator = new SimpleReportGenerator(); |
| private final List<ImageEntry> images; |
| private final PrintWriter writer; |
| private long startTime = 0; |
| |
| private final Map<String, Long> totalWaitTime = new HashMap<String, Long>(); |
| private int maxMethodNameLength = 0; |
| private int maxTotalTimeLength = 0; |
| |
| public RcpttReportGenerator(PrintWriter writer, List<ImageEntry> images) { |
| this.writer = writer; |
| this.images = images; |
| } |
| |
| protected PrintWriter writeTabs(int tabs) { |
| return writeTabs(writer, tabs); |
| } |
| |
| public void writeReport(Report report, int tabs) { |
| startTime = report.getRoot().getStartTime(); |
| printNode(report.getRoot(), tabs); |
| printTotalWaitTime(); |
| } |
| |
| protected static <T extends Appendable> T writeTabs(T writer, int tabs) { |
| for (int i = 0; i < tabs; ++i) { |
| try { |
| writer.append(" "); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| return writer; |
| } |
| |
| // @Override |
| // public StringBuilder toString(StringBuilder builder, |
| // int tabs, org.eclipse.emf.ecore.EObject obj, |
| // String... ignores) { |
| // if (obj instanceof AdvancedInformation) { |
| // String content = new AdvancedInformationGenerator() |
| // .generateContent((AdvancedInformation) obj); |
| // builder.append(content); |
| // return builder; |
| // } |
| // return super.toString(builder, tabs, obj, ignores); |
| // }; |
| // |
| public void printNode(Node infoNode, int tabs) { |
| writeQ7Info(tabs, infoNode); |
| |
| writeQ7WaitInfo(tabs, infoNode); |
| |
| writeLogsFromNode(tabs, infoNode); |
| |
| try { |
| for (Event child : infoNode.getEvents()) { |
| writeEvent(child, tabs + 1); |
| } |
| |
| for (Snaphot child : infoNode.getSnapshots()) { |
| writeSnapshot(child, tabs + 1); |
| } |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| printChildren(tabs, infoNode); |
| } |
| |
| private void writeEvent(Event event, int tabs) throws IOException { |
| if (event.getCount() == 1) { |
| writeTabs(tabs + 1) |
| .append("Event at ") |
| .println(TimeFormatHelper.format(event.getTime() - startTime)); |
| } else { |
| writeTabs(tabs + 1).append("Event: ") |
| .append(String.valueOf(event.getCount())) |
| .append(" times, first at ") |
| .append(TimeFormatHelper.format(event.getTime() - startTime)) |
| .println(); |
| } |
| printObject(event.getData(), tabs + 2); |
| } |
| |
| public void printObject(EObject object, int tabs) throws IOException { |
| if (object instanceof EclipseStatus) { |
| printStatus((EclipseStatus) object, tabs); |
| } else if (object instanceof Snaphot) { |
| writeSnapshot((Snaphot) object, tabs); |
| } else { |
| simpleReportGenerator.toString(writer, tabs, object); |
| } |
| |
| } |
| |
| private void writeSnapshot(Snaphot snapshot, int tabs) throws IOException { |
| if (snapshot.getData() instanceof Screenshot) { |
| Screenshot shot = (Screenshot) snapshot |
| .getData(); |
| String description = shot.getMessage() |
| + ": " //$NON-NLS-1$ |
| + TimeFormatHelper.format(snapshot |
| .getTime() |
| - startTime); |
| images.add(new ImageEntry(shot.getData(), description)); |
| return; |
| } else if (snapshot.getData() instanceof AdvancedInformation) { |
| printAdvanced((AdvancedInformation) snapshot.getData(), tabs); |
| return; |
| } |
| writeTabs(tabs).println("Snapshot " + TimeFormatHelper.format(snapshot.getTime() - startTime)); |
| printObject(snapshot.getData(), tabs + 1); |
| } |
| |
| private void printAdvanced(AdvancedInformation data, int tabs) { |
| new AdvancedInformationGenerator(writer).writeAdvanced(data, tabs); |
| } |
| |
| private PrintWriter w(int tabs) { |
| return writeTabs(tabs); |
| } |
| |
| private void printStatus(EclipseStatus status, int tabs) throws IOException { |
| SimpleSeverity severity = SimpleSeverity.create(status.getSeverity()); |
| w(tabs).append(severity.name()); |
| writer.append(" in plugin: ").println(status.getPlugin()); |
| w(tabs).append("message: ").println(status.getMessage()); |
| if (status.getException() != null) { |
| w(tabs).println("exception: "); |
| printJavaException(status.getException(), tabs + 2); |
| } |
| for (EclipseStatus child : status.getChildren()) { |
| printStatus(child, tabs + 1); |
| } |
| } |
| |
| private void printJavaException(JavaException e, int tabs) { |
| w(tabs).append(e.getClassName()); |
| if (!StringUtils.isEmpty(e.getMessage())) { |
| writer.print(":" + e.getMessage()); |
| } |
| writer.println(); |
| for (JavaStackTraceEntry st : e.getStackTrace()) { |
| w(tabs + 2).append("at ") |
| .append(st.getClassName()).append(".") |
| .append(st.getMethodName()).append("(") |
| .append(st.getFileName()).append(":") |
| .append("" + st.getLineNumber()).append(")") |
| .println(); |
| } |
| JavaException cause = e.getCause(); |
| if (cause != null) { |
| w(tabs + 2).println("Caused by:"); |
| printJavaException(cause, tabs + 1); |
| } |
| } |
| |
| protected void printChildren(int tabs, Node infoNode) { |
| for (Node child : infoNode.getChildren()) { |
| printNode(child, tabs + 4); |
| } |
| } |
| |
| private void writeQ7WaitInfo(int tabs, Node infoNode) { |
| Q7WaitInfoRoot waitInfo = ReportHelper.getWaitInfo(infoNode, false); |
| if (waitInfo != null) { |
| writeQ7WaitInfo(tabs, waitInfo); //$NON-NLS-1$ |
| } |
| } |
| |
| public static String getType(Q7WaitInfoRoot info, Q7WaitInfo q7WaitInfo) { |
| String type = info.getTypesNames().get(q7WaitInfo.getTypeId()); |
| if (!TeslaFeatures.isIncludeIgnoredWaitDetails() && type.contains("(ignored)")) { |
| return null; |
| } |
| return type; |
| } |
| |
| public void writeQ7WaitInfo(int tabs, Q7WaitInfoRoot info) { |
| List<Q7WaitInfo> infos = new ArrayList<Q7WaitInfo>(info.getInfos()); |
| if (infos.size() == 0) { |
| return; |
| } |
| Comparator<Q7WaitInfo> comparator = new Comparator<Q7WaitInfo>() { |
| @Override |
| public int compare(Q7WaitInfo info1, Q7WaitInfo info2) { |
| return Long.compare(info1.getDuration(), info2.getDuration()); |
| } |
| }; |
| Collections.sort(infos, Collections.reverseOrder(comparator)); |
| |
| String classNameColumn = "Method name"; //$NON-NLS-1$ |
| String totalTimeColumn = "Time"; //$NON-NLS-1$ |
| int classNameLength = classNameColumn.length(); |
| int totalTimeLength = totalTimeColumn.length(); |
| boolean isEmpty = true; |
| |
| for (Q7WaitInfo q7WaitInfo : infos) { |
| long totalTime = q7WaitInfo.getDuration(); |
| String type = getType(info, q7WaitInfo); |
| String className = SimpleReportGenerator.getClassName(info, q7WaitInfo); |
| if (type == null) { |
| continue; |
| } |
| if (!TeslaFeatures.isIncludeEclipseMethodsWaitDetails() |
| && className.startsWith("org.eclipse")) { //$NON-NLS-1$ |
| continue; |
| } |
| if (totalTime == 0) { |
| continue; |
| } |
| |
| // calculate column length |
| String methodName = String.format("%s: %s", type, className); |
| if (methodName.length() > classNameLength) { |
| classNameLength = methodName.length(); |
| } |
| if (String.valueOf(totalTime).length() > totalTimeLength) { |
| totalTimeLength = String.valueOf(totalTime).length(); |
| } |
| |
| isEmpty = false; |
| } |
| if (isEmpty) { |
| return; |
| } |
| |
| writeTabs(tabs + 4).println("--> Wait details <--"); |
| writeTabs(tabs + 8) |
| .append(String.format("%" + -classNameLength + "s", classNameColumn)) |
| .append(" ") |
| .append(String.format("%" + -totalTimeLength + "s", totalTimeColumn)) |
| .println(); |
| |
| for (Q7WaitInfo i : infos) { |
| long totalTime = i.getDuration(); |
| String type = getType(info, i); |
| String className = SimpleReportGenerator.getClassName(info, i); |
| if (type == null) { |
| continue; |
| } |
| if (!TeslaFeatures.isIncludeEclipseMethodsWaitDetails() |
| && className.startsWith("org.eclipse")) { //$NON-NLS-1$ |
| continue; |
| } |
| if (totalTime == 0) { |
| continue; |
| } |
| |
| String methodName = String.format("%s: %s", type, className); |
| writeTabs(tabs + 8) |
| .append(String.format("%" + -classNameLength + "s", methodName)) |
| .append(" ") |
| .append(String.format("%" + totalTimeLength + "s", totalTime)) |
| .println(); |
| |
| addWaitTime(type, className, totalTime); |
| } |
| } |
| |
| String kindToString(ItemKind kind, String name) { |
| switch (kind) { |
| case CONTEXT: |
| return "Context *" + name + "*"; |
| case ECL_COMMAND: |
| return name; |
| case SCRIPT: |
| return "Script *" + name + "*"; |
| case TESTCASE: |
| return "Test case *" + name + "*"; |
| case TEST_SUITE: |
| return "Test suite *" + name + "*"; |
| case VERIFICATION: |
| return "Verification *" + name + "*"; |
| } |
| return name; |
| } |
| |
| private void writeQ7Info(int tabs, Node infoNode) { |
| Q7Info q7Info = ReportHelper.getInfo(infoNode); |
| writeTabs(tabs); |
| if (q7Info != null) { |
| writer.append(kindToString(q7Info.getType(), infoNode.getName())); |
| writer. |
| append(" ") |
| .append("time: " + |
| TimeFormatHelper.format(infoNode.getDuration())) |
| .println(); |
| writeResult(tabs + 1, q7Info.getResult()); |
| } |
| } |
| |
| public void writeResult(int tabs, ProcessStatus result) { |
| if (result == null) |
| result = RcpttPlugin.createProcessStatus(IStatus.ERROR, "Null result"); |
| if (SimpleSeverity.create(result) == SimpleSeverity.OK) |
| return; |
| w(tabs).append("Result: " + SimpleSeverity.create(result).name() + ", message: ") |
| .println(result.getMessage()); |
| writeException(writer, tabs + 1, result.getException()); |
| for (ProcessStatus child : result.getChildren()) { |
| writeResult(tabs + 1, child); |
| } |
| } |
| |
| private static void writeException(Writer writer, final int tabs, EclException exception) { |
| if (exception == null) |
| return; |
| IndentedWriter iwriter = new IndentedWriter(writer) { |
| @Override |
| public void writeIndent() { |
| writeTabs(this, tabs); |
| } |
| }; |
| ProcessStatusConverter.getThrowable(exception).printStackTrace(iwriter); |
| iwriter.println(); |
| iwriter.flush(); |
| } |
| |
| public void writeLogsFromNode(int tabs, Node infoNode) { |
| boolean haveEntries = false; |
| for (LoggingCategory logCategory : LoggingCategory.VALUES) { |
| String log = ReportBuilder.getLogs(infoNode, logCategory); |
| if (!StringUtils.isEmpty(log)) { |
| if (!haveEntries) { |
| haveEntries = true; |
| writeTabs(tabs) |
| .println("--------------Logs BEGIN-------------------"); //$NON-NLS-1$ |
| } |
| |
| for (String logLine : log.split("[\r\n]+")) { |
| writeTabs(tabs) |
| .append(logLine) |
| .println(); |
| } |
| } |
| } |
| |
| if (haveEntries) { |
| writeTabs(tabs) |
| .append("--------------Logs END-------------------") //$NON-NLS-1$ |
| .println(); |
| } |
| } |
| |
| private void addWaitTime(String type, String className, long totalTime) { |
| if (!(type.equals("job") || type.equals("sync") || type.equals("async"))) { //$NON-NLS-1$ |
| return; |
| } |
| if (!TeslaFeatures.isIncludeEclipseMethodsWaitDetails() |
| && className.startsWith("org.eclipse")) { //$NON-NLS-1$ |
| return; |
| } |
| |
| String methodName = String.format("%s: %s", type, className); |
| if (totalWaitTime.containsKey(methodName)) { |
| totalTime += totalWaitTime.get(methodName); |
| } |
| totalWaitTime.put(methodName, totalTime); |
| |
| int methodNameLength = methodName.length(); |
| if (methodNameLength > maxMethodNameLength) { |
| maxMethodNameLength = methodNameLength; |
| } |
| int totalTimeLength = String.valueOf(totalTime).length(); |
| if (totalTimeLength > maxTotalTimeLength) { |
| maxTotalTimeLength = totalTimeLength; |
| } |
| } |
| |
| private void printTotalWaitTime() { |
| String totalWaitTimeTable = "Total wait time:"; //$NON-NLS-1$ |
| String methodNameColumn = "Method name"; //$NON-NLS-1$ |
| String totalTimeColumn = "Time"; //$NON-NLS-1$ |
| String noWaitInfoMessage = TeslaFeatures.isIncludeEclipseMethodsWaitDetails() |
| ? "There were no methods RCPTT was waiting for" |
| : "There were no third-party methods RCPTT was waiting for"; |
| |
| int methodNameLength = methodNameColumn.length(); |
| if (methodNameLength > maxMethodNameLength) { |
| maxMethodNameLength = methodNameLength; |
| } |
| int totalTimeLength = totalTimeColumn.length(); |
| if (totalTimeLength > maxTotalTimeLength) { |
| maxTotalTimeLength = totalTimeLength; |
| } |
| |
| writer.println(); |
| writer.append(totalWaitTimeTable).println(); |
| |
| if (totalWaitTime.isEmpty()) { |
| writer.append(" ") |
| .append(noWaitInfoMessage) // $NON-NLS-1$ |
| .println(); |
| return; |
| } |
| |
| writer.append(" ") |
| .append(String.format("%" + -maxMethodNameLength + "s", methodNameColumn)) |
| .append(" ") |
| .append(String.format("%" + -maxTotalTimeLength + "s", totalTimeColumn)) |
| .println(); |
| List<Map.Entry<String, Long>> sortedTable = getSortedTimeTable(totalWaitTime); |
| for (Map.Entry<String, Long> entry : sortedTable) { |
| writer.append(" ") |
| .append(String.format("%" + -maxMethodNameLength + "s", entry.getKey())) |
| .append(" ") |
| .append(String.format("%" + maxTotalTimeLength + "s", entry.getValue())) |
| .println(); |
| } |
| } |
| |
| private List<Map.Entry<String, Long>> getSortedTimeTable(Map<String, Long> unsortedMap) { |
| Comparator<Map.Entry<String, Long>> comparator = new Comparator<Map.Entry<String, Long>>() { |
| |
| @Override |
| public int compare(Map.Entry<String, Long> entry1, Map.Entry<String, Long> entry2) { |
| return entry1.getValue().compareTo(entry2.getValue()); |
| } |
| |
| }; |
| |
| List<Map.Entry<String, Long>> sortedTable = new LinkedList<>(unsortedMap.entrySet()); |
| Collections.sort(sortedTable, Collections.reverseOrder(comparator)); |
| return sortedTable; |
| } |
| |
| } |