/* * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.graalvm.compiler.debug.internal; import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueFile; import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueHumanReadable; import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueSummary; import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueThreadFilter; import static org.graalvm.compiler.debug.GraalDebugConfig.Options.SuppressZeroDebugValues; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.graalvm.compiler.debug.CSVUtil; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.debug.LogStream; import org.graalvm.compiler.debug.TTY; import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl; import org.graalvm.compiler.debug.internal.method.MethodMetricsPrinter; /** * Facility for printing the {@linkplain KeyRegistry#getDebugValues() values} collected across all * {@link DebugValueMap#getTopLevelMaps() threads}. */ public class DebugValuesPrinter { private static final String COMPUTER_READABLE_FMT = CSVUtil.buildFormatString("%s", "%s", "%s", "%s"); private static final char SCOPE_DELIMITER = '.'; private final MethodMetricsPrinter mmPrinter; public DebugValuesPrinter() { this(null); } public DebugValuesPrinter(MethodMetricsPrinter mmPrinter) { this.mmPrinter = mmPrinter; } public void printDebugValues() throws GraalError { TTY.println(); TTY.println(""); List topLevelMaps = DebugValueMap.getTopLevelMaps(); List debugValues = KeyRegistry.getDebugValues(); if (debugValues.size() > 0) { try { ArrayList sortedValues = new ArrayList<>(debugValues); Collections.sort(sortedValues); String summary = DebugValueSummary.getValue(); if (summary == null) { summary = "Complete"; } if (DebugValueThreadFilter.getValue() != null && topLevelMaps.size() != 0) { topLevelMaps = topLevelMaps.stream().filter(map -> Pattern.compile(DebugValueThreadFilter.getValue()).matcher(map.getName()).find()).collect(Collectors.toList()); if (topLevelMaps.size() == 0) { TTY.println("Warning: DebugValueThreadFilter=%s eliminated all maps so nothing will be printed", DebugValueThreadFilter.getValue()); } } switch (summary) { case "Name": { LogStream log = getLogStream(); printSummary(log, topLevelMaps, sortedValues); break; } case "Partial": { DebugValueMap globalMap = new DebugValueMap("Global"); for (DebugValueMap map : topLevelMaps) { flattenChildren(map, globalMap); } globalMap.normalize(); LogStream log = getLogStream(); printMap(log, new DebugValueScope(null, globalMap), sortedValues); break; } case "Complete": { DebugValueMap globalMap = new DebugValueMap("Global"); for (DebugValueMap map : topLevelMaps) { globalMap.addChild(map); } globalMap.group(); globalMap.normalize(); LogStream log = getLogStream(); printMap(log, new DebugValueScope(null, globalMap), sortedValues); break; } case "Thread": for (DebugValueMap map : topLevelMaps) { TTY.println("Showing the results for thread: " + map.getName()); map.group(); map.normalize(); LogStream log = getLogStream(map.getName().replace(' ', '_')); printMap(log, new DebugValueScope(null, map), sortedValues); } break; default: throw new GraalError("Unknown summary type: %s", summary); } for (DebugValueMap topLevelMap : topLevelMaps) { topLevelMap.reset(); } } catch (Throwable e) { // Don't want this to change the exit status of the VM PrintStream err = System.err; err.println("Error while printing debug values:"); e.printStackTrace(); } } if (mmPrinter != null) { mmPrinter.printMethodMetrics(MethodMetricsImpl.collectedMetrics()); } TTY.println(""); } private static LogStream getLogStream() { return getLogStream(null); } private static LogStream getLogStream(String prefix) { String debugValueFile = DebugValueFile.getValue(); if (debugValueFile != null) { try { final String fileName; if (prefix != null) { fileName = prefix + '-' + debugValueFile; } else { fileName = debugValueFile; } LogStream logStream = new LogStream(new FileOutputStream(fileName)); TTY.println("Writing debug values to '%s'", fileName); return logStream; } catch (FileNotFoundException e) { TTY.println("Warning: Could not open debug value log file: %s (defaulting to TTY)", e.getMessage()); } } return TTY.out(); } private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) { globalMap.addChild(map); for (DebugValueMap child : map.getChildren()) { flattenChildren(child, globalMap); } map.clearChildren(); } private void printSummary(LogStream log, List topLevelMaps, List debugValues) { DebugValueMap result = new DebugValueMap("Summary"); for (int i = debugValues.size() - 1; i >= 0; i--) { DebugValue debugValue = debugValues.get(i); int index = debugValue.getIndex(); long total = collectTotal(topLevelMaps, index); result.setCurrentValue(index, total); } printMap(log, new DebugValueScope(null, result), debugValues); } private long collectTotal(List maps, int index) { long total = 0; for (int i = 0; i < maps.size(); i++) { DebugValueMap map = maps.get(i); total += map.getCurrentValue(index); total += collectTotal(map.getChildren(), index); } return total; } /** * Tracks the scope when printing a {@link DebugValueMap}, allowing "empty" scopes to be * omitted. An empty scope is one in which there are no (nested) non-zero debug values. */ static class DebugValueScope { final DebugValueScope parent; final int level; final DebugValueMap map; private boolean printed; DebugValueScope(DebugValueScope parent, DebugValueMap map) { this.parent = parent; this.map = map; this.level = parent == null ? 0 : parent.level + 1; } public void print(LogStream log) { if (!printed) { printed = true; if (parent != null) { parent.print(log); } printIndent(log, level); log.printf("%s%n", map.getName()); } } public String toRawString() { return toRaw(new StringBuilder()).toString(); } private StringBuilder toRaw(StringBuilder stringBuilder) { final StringBuilder sb = (parent == null) ? stringBuilder : parent.toRaw(stringBuilder).append(SCOPE_DELIMITER); return sb.append(map.getName()); } } private void printMap(LogStream log, DebugValueScope scope, List debugValues) { if (DebugValueHumanReadable.getValue()) { printMapHumanReadable(log, scope, debugValues); } else { printMapComputerReadable(log, scope, debugValues); } } private void printMapComputerReadable(LogStream log, DebugValueScope scope, List debugValues) { for (DebugValue value : debugValues) { long l = scope.map.getCurrentValue(value.getIndex()); if (l != 0 || !SuppressZeroDebugValues.getValue()) { CSVUtil.Escape.println(log, COMPUTER_READABLE_FMT, scope.toRawString(), value.getName(), value.toRawString(l), value.rawUnit()); } } List children = scope.map.getChildren(); for (int i = 0; i < children.size(); i++) { DebugValueMap child = children.get(i); printMapComputerReadable(log, new DebugValueScope(scope, child), debugValues); } } private void printMapHumanReadable(LogStream log, DebugValueScope scope, List debugValues) { for (DebugValue value : debugValues) { long l = scope.map.getCurrentValue(value.getIndex()); if (l != 0 || !SuppressZeroDebugValues.getValue()) { scope.print(log); printIndent(log, scope.level + 1); log.println(value.getName() + "=" + value.toString(l)); } } List children = scope.map.getChildren(); for (int i = 0; i < children.size(); i++) { DebugValueMap child = children.get(i); printMapHumanReadable(log, new DebugValueScope(scope, child), debugValues); } } private static void printIndent(LogStream log, int level) { for (int i = 0; i < level; ++i) { log.print(" "); } log.print("|-> "); } public void clearDebugValues() { List topLevelMaps = DebugValueMap.getTopLevelMaps(); List debugValues = KeyRegistry.getDebugValues(); if (debugValues.size() > 0) { for (DebugValueMap map : topLevelMaps) { map.reset(); } } if (mmPrinter != null) { MethodMetricsImpl.clearMM(); } } }