1 /* 2 * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved. 3 * 4 * This file is part of the Lock Contention Tracing Subsystem for the HotSpot 5 * Virtual Machine, which is developed at Christian Doppler Laboratory on 6 * Monitoring and Evolution of Very-Large-Scale Software Systems. Please 7 * contact us at <http://mevss.jku.at/> if you need additional information 8 * or have any questions. 9 * 10 * This code is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License version 2 only, as 12 * published by the Free Software Foundation. 13 * 14 * This code is distributed in the hope that it will be useful, but WITHOUT 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 * version 2 for more details (a copy is included in the LICENSE file that 18 * accompanied this code). 19 * 20 * You should have received a copy of the GNU General Public License version 21 * 2 along with this work. If not, see <http://www.gnu.org/licenses/>. 22 * 23 */ 24 package sun.evtracing.processing.statistics; 25 26 import java.io.OutputStream; 27 import java.io.PrintStream; 28 import java.util.Arrays; 29 import java.util.Comparator; 30 import java.util.Map; 31 import java.util.Map.Entry; 32 import java.util.Set; 33 import java.util.stream.Collectors; 34 35 import sun.evtracing.parser.metadata.JavaStack; 36 import sun.evtracing.parser.metadata.JavaStackFrame; 37 import sun.evtracing.parser.metadata.JavaThread; 38 import sun.evtracing.processing.statistics.aggregator.Aggregator; 39 import sun.evtracing.processing.statistics.metadata.JavaClassStack; 40 41 public class StatisticsPrinter { 42 43 private static final int REPEAT_HEADING_EVERY_N_LINES = 50; 44 45 private final Statistics statistics; 46 private final PrintStream out; 47 48 public StatisticsPrinter(Statistics stats, OutputStream out) { 49 this.statistics = stats; 50 this.out = new PrintStream(out); 51 } 52 53 public void print() { 54 printContendingThreadStatistics(); 55 printBlockerClassesStatistics(); 56 } 57 58 private void printContendingThreadStatistics() { 59 int i = 0; 60 for (Entry<JavaThread, Aggregator> e : statistics.contendingThreads().entrySet()) { 61 JavaThread t = e.getKey(); 62 NumberStatistic s = e.getValue().statistic(); 63 if (i++ % REPEAT_HEADING_EVERY_N_LINES == 0) { 64 out.printf("===== THREADS (overall contention) =============================================%n"); 65 out.printf(" tid threadname | runtime | contTotal cont%% contAvg contMax |%n"); 66 out.printf("--------------------------------------------------------------------------------%n"); 67 } 68 out.printf("%6d %-20s | %8.1f | %9s %6s %8s %7s%n", t.threadId(), limitLength(nullToString(t.name()), 20), 69 toMs(t.totalRuntime()), 70 formatOrDashIfZero("%9.1f", toMs(s.getSum())), formatOrDashIfZero("%5.1f%%", asPct(s.getSum(), t.totalRuntime())), formatOrDashIfNaN("%8.2f", toMs(s.getAverage())), formatOrDashIfZero("%7.1f", toMs(s.getMax()))); 71 } 72 out.println(); 73 } 74 75 private void printBlockerHeading(int i) { 76 if (i % REPEAT_HEADING_EVERY_N_LINES == 0) { 77 out.printf("===== BLOCKER CLASSES, CONTENDING SITE, OWNER SITE ==========================================================================%n"); 78 out.printf(" blocker class / contending site / owner site | contTotal cont%% contAvg contMax%n"); 79 out.printf("-----------------------------------------------------------------------------------------------------------------------------%n"); 80 } 81 } 82 83 private void printBlockerClassesStatistics() { 84 NumberStatistic total = new NumberStatistic(); 85 for (Aggregator agg : statistics.blockerClasses().values()) { 86 total = NumberStatistic.merge(total, agg.statistic()); 87 } 88 89 int i = 0; 90 printBlockerHeading(i++); 91 out.printf("%-84s | %10.1f %6s %8.2f %7.1f%n", "<total>", 92 toMs(total.getSum()), formatOrDashIfZero("%5.1f%%", 100), toMs(total.getAverage()), toMs(total.getMax())); 93 94 for (Entry<JavaClassStack, Aggregator> e0 : sortByTotalDescending(statistics.blockerClasses().entrySet())) { 95 JavaClassStack k0 = e0.getKey(); 96 NumberStatistic s0 = e0.getValue().statistic(); 97 98 printBlockerHeading(i++); 99 String classes = Arrays.stream(k0.classes()).map(c -> c.prettyName(true)).collect(Collectors.joining(", ")); 100 out.printf(" %-82s | %10.1f %6s %8.2f %7.1f%n", limitLength(classes, 82), 101 toMs(s0.getSum()), formatOrDashIfZero("%5.1f%%", asPct(s0.getSum(), total.getSum())), toMs(s0.getAverage()), toMs(s0.getMax())); 102 103 Map<?, Aggregator> contendingSites = e0.getValue().children(); 104 for (Entry<?, Aggregator> e1 : sortByTotalDescending(contendingSites.entrySet())) { 105 JavaStack k1 = (JavaStack) e1.getKey(); 106 NumberStatistic s1 = e1.getValue().statistic(); 107 108 printBlockerHeading(i++); 109 out.printf(" %-80s | %10.1f %6s %8.2f %7.1f%n", stackAsString(k1, 80), 110 toMs(s1.getSum()), formatOrDashIfZero("%5.1f%%", asPct(s1.getSum(), total.getSum())), toMs(s1.getAverage()), toMs(s1.getMax())); 111 112 Map<?, Aggregator> ownerSites = e1.getValue().children(); 113 for (Entry<?, Aggregator> e2 : sortByTotalDescending(ownerSites.entrySet())) { 114 JavaStack k2 = (JavaStack) e2.getKey(); 115 NumberStatistic s2 = e2.getValue().statistic(); 116 117 printBlockerHeading(i++); 118 out.printf(" %-78s | %10.1f %6s %8.2f %7.1f%n", stackAsString(k2, 78), 119 toMs(s2.getSum()), formatOrDashIfZero("%5.1f%%", asPct(s2.getSum(), total.getSum())), toMs(s2.getAverage()), toMs(s2.getMax())); 120 } 121 } 122 } 123 out.println(); 124 } 125 126 private<T> Iterable<Entry<T, Aggregator>> sortByTotalDescending(Set<Entry<T, Aggregator>> set) { 127 Comparator<? super Entry<T, Aggregator>> cmp = ((x, y) -> Long.signum(y.getValue().statistic().getSum() - x.getValue().statistic().getSum())); 128 return set.stream().sorted(cmp)::iterator; 129 } 130 131 private String stackAsString(JavaStack stack, int maxLength) { 132 if (stack.isUnknown()) { 133 return ("<unknown>"); 134 } 135 if (!stack.isSet()) { 136 return ("<unset>"); 137 } 138 139 StringBuilder builder = new StringBuilder(); 140 int count = 0; 141 for (JavaStackFrame frame : stack) { 142 String descriptor = frame.method().prettyDescriptor(true); 143 if (builder.length() != 0) { 144 builder.append(", "); 145 if (builder.length() + descriptor.length() > maxLength) { 146 String remaining = String.format("[+%d]", stack.frameCount() - count); 147 if (builder.length() > maxLength - remaining.length()) { 148 builder.setLength(maxLength - remaining.length()); 149 } 150 builder.append(remaining); 151 break; 152 } 153 } 154 builder.append(descriptor); 155 count++; 156 } 157 return builder.toString(); 158 } 159 160 private double asPct(double value, double total) { 161 return value * 100 / total; 162 } 163 164 private double toMs(double nanoseconds) { 165 return nanoseconds / 1000000.0; 166 } 167 168 private String formatOrDashIfZero(String format, double value) { 169 if (value == 0) { 170 return "-"; 171 } 172 return formatOrDashIfNaN(format, value); 173 } 174 175 private String formatOrDashIfNaN(String format, double value) { 176 if (Double.isNaN(value)) { 177 return "-"; 178 } 179 return String.format(format, value); 180 181 } 182 183 private String limitLength(String value, int maxLength) { 184 if (value.length() > maxLength) { 185 return value.substring(0, maxLength - 2) + ".."; 186 } else { 187 return value; 188 } 189 } 190 191 private String nullToString(String value) { 192 if (value == null) { 193 return "<null>"; 194 } 195 return value; 196 } 197 198 }