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 }