1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.debug.internal; 24 25 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueFile; 26 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueHumanReadable; 27 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueSummary; 28 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.DebugValueThreadFilter; 29 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.SuppressZeroDebugValues; 30 31 import java.io.FileNotFoundException; 32 import java.io.FileOutputStream; 33 import java.io.PrintStream; 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 import java.util.regex.Pattern; 38 import java.util.stream.Collectors; 39 40 import org.graalvm.compiler.debug.CSVUtil; 41 import org.graalvm.compiler.debug.GraalError; 42 import org.graalvm.compiler.debug.LogStream; 43 import org.graalvm.compiler.debug.TTY; 44 import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl; 45 import org.graalvm.compiler.debug.internal.method.MethodMetricsPrinter; 46 47 /** 48 * Facility for printing the {@linkplain KeyRegistry#getDebugValues() values} collected across all 49 * {@link DebugValueMap#getTopLevelMaps() threads}. 50 */ 51 public class DebugValuesPrinter { 52 private static final String COMPUTER_READABLE_FMT = CSVUtil.buildFormatString("%s", "%s", "%s", "%s"); 53 private static final char SCOPE_DELIMITER = '.'; 54 private final MethodMetricsPrinter mmPrinter; 55 56 public DebugValuesPrinter() { 57 this(null); 58 } 59 60 public DebugValuesPrinter(MethodMetricsPrinter mmPrinter) { 61 this.mmPrinter = mmPrinter; 62 } 63 64 public void printDebugValues() throws GraalError { 65 TTY.println(); 66 TTY.println("<DebugValues>"); 67 List<DebugValueMap> topLevelMaps = DebugValueMap.getTopLevelMaps(); 68 List<DebugValue> debugValues = KeyRegistry.getDebugValues(); 69 if (debugValues.size() > 0) { 70 try { 71 ArrayList<DebugValue> sortedValues = new ArrayList<>(debugValues); 72 Collections.sort(sortedValues); 73 74 String summary = DebugValueSummary.getValue(); 75 if (summary == null) { 76 summary = "Complete"; 77 } 78 if (DebugValueThreadFilter.getValue() != null && topLevelMaps.size() != 0) { 79 topLevelMaps = topLevelMaps.stream().filter(map -> Pattern.compile(DebugValueThreadFilter.getValue()).matcher(map.getName()).find()).collect(Collectors.toList()); 80 if (topLevelMaps.size() == 0) { 81 TTY.println("Warning: DebugValueThreadFilter=%s eliminated all maps so nothing will be printed", DebugValueThreadFilter.getValue()); 82 } 83 } 84 switch (summary) { 85 case "Name": { 86 LogStream log = getLogStream(); 87 printSummary(log, topLevelMaps, sortedValues); 88 break; 89 } 90 case "Partial": { 91 DebugValueMap globalMap = new DebugValueMap("Global"); 92 for (DebugValueMap map : topLevelMaps) { 93 flattenChildren(map, globalMap); 94 } 95 globalMap.normalize(); 96 LogStream log = getLogStream(); 97 printMap(log, new DebugValueScope(null, globalMap), sortedValues); 98 break; 99 } 100 case "Complete": { 101 DebugValueMap globalMap = new DebugValueMap("Global"); 102 for (DebugValueMap map : topLevelMaps) { 103 globalMap.addChild(map); 104 } 105 globalMap.group(); 106 globalMap.normalize(); 107 LogStream log = getLogStream(); 108 printMap(log, new DebugValueScope(null, globalMap), sortedValues); 109 break; 110 } 111 case "Thread": 112 for (DebugValueMap map : topLevelMaps) { 113 TTY.println("Showing the results for thread: " + map.getName()); 114 map.group(); 115 map.normalize(); 116 LogStream log = getLogStream(map.getName().replace(' ', '_')); 117 printMap(log, new DebugValueScope(null, map), sortedValues); 118 } 119 break; 120 default: 121 throw new GraalError("Unknown summary type: %s", summary); 122 } 123 for (DebugValueMap topLevelMap : topLevelMaps) { 124 topLevelMap.reset(); 125 } 126 } catch (Throwable e) { 127 // Don't want this to change the exit status of the VM 128 PrintStream err = System.err; 129 err.println("Error while printing debug values:"); 130 e.printStackTrace(); 131 } 132 } 133 if (mmPrinter != null) { 134 mmPrinter.printMethodMetrics(MethodMetricsImpl.collectedMetrics()); 135 } 136 TTY.println("</DebugValues>"); 137 } 138 139 private static LogStream getLogStream() { 140 return getLogStream(null); 141 } 142 143 private static LogStream getLogStream(String prefix) { 144 String debugValueFile = DebugValueFile.getValue(); 145 if (debugValueFile != null) { 146 try { 147 final String fileName; 148 if (prefix != null) { 149 fileName = prefix + '-' + debugValueFile; 150 } else { 151 fileName = debugValueFile; 152 } 153 LogStream logStream = new LogStream(new FileOutputStream(fileName)); 154 TTY.println("Writing debug values to '%s'", fileName); 155 return logStream; 156 } catch (FileNotFoundException e) { 157 TTY.println("Warning: Could not open debug value log file: %s (defaulting to TTY)", e.getMessage()); 158 } 159 } 160 return TTY.out(); 161 } 162 163 private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) { 164 globalMap.addChild(map); 165 for (DebugValueMap child : map.getChildren()) { 166 flattenChildren(child, globalMap); 167 } 168 map.clearChildren(); 169 } 170 171 private void printSummary(LogStream log, List<DebugValueMap> topLevelMaps, List<DebugValue> debugValues) { 172 DebugValueMap result = new DebugValueMap("Summary"); 173 for (int i = debugValues.size() - 1; i >= 0; i--) { 174 DebugValue debugValue = debugValues.get(i); 175 int index = debugValue.getIndex(); 176 long total = collectTotal(topLevelMaps, index); 177 result.setCurrentValue(index, total); 178 } 179 printMap(log, new DebugValueScope(null, result), debugValues); 180 } 181 182 private long collectTotal(List<DebugValueMap> maps, int index) { 183 long total = 0; 184 for (int i = 0; i < maps.size(); i++) { 185 DebugValueMap map = maps.get(i); 186 total += map.getCurrentValue(index); 187 total += collectTotal(map.getChildren(), index); 188 } 189 return total; 190 } 191 192 /** 193 * Tracks the scope when printing a {@link DebugValueMap}, allowing "empty" scopes to be 194 * omitted. An empty scope is one in which there are no (nested) non-zero debug values. 195 */ 196 static class DebugValueScope { 197 198 final DebugValueScope parent; 199 final int level; 200 final DebugValueMap map; 201 private boolean printed; 202 203 DebugValueScope(DebugValueScope parent, DebugValueMap map) { 204 this.parent = parent; 205 this.map = map; 206 this.level = parent == null ? 0 : parent.level + 1; 207 } 208 209 public void print(LogStream log) { 210 if (!printed) { 211 printed = true; 212 if (parent != null) { 213 parent.print(log); 214 } 215 printIndent(log, level); 216 log.printf("%s%n", map.getName()); 217 } 218 } 219 220 public String toRawString() { 221 return toRaw(new StringBuilder()).toString(); 222 } 223 224 private StringBuilder toRaw(StringBuilder stringBuilder) { 225 final StringBuilder sb = (parent == null) ? stringBuilder : parent.toRaw(stringBuilder).append(SCOPE_DELIMITER); 226 return sb.append(map.getName()); 227 } 228 229 } 230 231 private void printMap(LogStream log, DebugValueScope scope, List<DebugValue> debugValues) { 232 if (DebugValueHumanReadable.getValue()) { 233 printMapHumanReadable(log, scope, debugValues); 234 } else { 235 printMapComputerReadable(log, scope, debugValues); 236 } 237 } 238 239 private void printMapComputerReadable(LogStream log, DebugValueScope scope, List<DebugValue> debugValues) { 240 241 for (DebugValue value : debugValues) { 242 long l = scope.map.getCurrentValue(value.getIndex()); 243 if (l != 0 || !SuppressZeroDebugValues.getValue()) { 244 CSVUtil.Escape.println(log, COMPUTER_READABLE_FMT, scope.toRawString(), value.getName(), value.toRawString(l), value.rawUnit()); 245 } 246 } 247 248 List<DebugValueMap> children = scope.map.getChildren(); 249 for (int i = 0; i < children.size(); i++) { 250 DebugValueMap child = children.get(i); 251 printMapComputerReadable(log, new DebugValueScope(scope, child), debugValues); 252 } 253 } 254 255 private void printMapHumanReadable(LogStream log, DebugValueScope scope, List<DebugValue> debugValues) { 256 257 for (DebugValue value : debugValues) { 258 long l = scope.map.getCurrentValue(value.getIndex()); 259 if (l != 0 || !SuppressZeroDebugValues.getValue()) { 260 scope.print(log); 261 printIndent(log, scope.level + 1); 262 log.println(value.getName() + "=" + value.toString(l)); 263 } 264 } 265 266 List<DebugValueMap> children = scope.map.getChildren(); 267 for (int i = 0; i < children.size(); i++) { 268 DebugValueMap child = children.get(i); 269 printMapHumanReadable(log, new DebugValueScope(scope, child), debugValues); 270 } 271 } 272 273 private static void printIndent(LogStream log, int level) { 274 for (int i = 0; i < level; ++i) { 275 log.print(" "); 276 } 277 log.print("|-> "); 278 } 279 280 public void clearDebugValues() { 281 List<DebugValueMap> topLevelMaps = DebugValueMap.getTopLevelMaps(); 282 List<DebugValue> debugValues = KeyRegistry.getDebugValues(); 283 if (debugValues.size() > 0) { 284 for (DebugValueMap map : topLevelMaps) { 285 map.reset(); 286 } 287 } 288 if (mmPrinter != null) { 289 MethodMetricsImpl.clearMM(); 290 } 291 } 292 }