1 /*
   2  * Copyright (c) 2016, 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 
  24 /*
  25  * @test TestDynamicGCThreadsStats
  26  * @bug 8176140
  27  * @summary Check that G1 reports per thread statistics.
  28  * @requires vm.gc=="G1" | vm.gc=="null"
  29  * @key gc
  30  * @library /testlibrary /test/lib
  31  * @build sun.hotspot.WhiteBox
  32  * @run main ClassFileInstaller sun.hotspot.WhiteBox
  33  * @run driver TestDynamicGCThreadsStats
  34  */
  35 
  36 import sun.hotspot.WhiteBox;
  37 
  38 import java.text.DecimalFormatSymbols;
  39 import java.util.regex.Matcher;
  40 import java.util.regex.Pattern;
  41 
  42 import jdk.test.lib.OutputAnalyzer;
  43 import jdk.test.lib.Platform;
  44 import jdk.test.lib.ProcessTools;
  45 
  46 import static jdk.test.lib.Asserts.*;
  47 
  48 public class TestDynamicGCThreadsStats {
  49 
  50     public static void runTest() throws Exception {
  51         final String[] arguments = {
  52             "-Xbootclasspath/a:.",
  53             "-XX:+UnlockExperimentalVMOptions",
  54             "-XX:+UnlockDiagnosticVMOptions",
  55             "-XX:+UseDynamicNumberOfGCThreads",
  56             "-XX:+WhiteBoxAPI",
  57             "-XX:+UseG1GC",
  58             "-Xlog:gc*=trace",
  59             "-Xmx10M",
  60             GCTest.class.getName()
  61             };
  62 
  63         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(arguments);
  64         OutputAnalyzer output = new OutputAnalyzer(pb.start());
  65 
  66         output.shouldHaveExitValue(0);
  67 
  68         // Looking for: 
  69         //        [0.234s][debug  ][gc,phases            ] GC(0)    Evacuate Collection Set: 3.7 ms
  70         output.shouldMatch("Evacuate Collection Set: \\d+\\.\\d+ms");
  71         // Find data with per thread times.
  72         // Any of the metrics with per thread times can be used.
  73         // Chose: 
  74         //        [0.234s][debug  ][gc,phases            ] GC(0)       Ext Root Scanning:        Min:  0.6, Avg:  1.1, Max:  1.6, Diff:  1.0, Sum:  2.2
  75         String stats = "Ext Root Scanning \\(ms\\):";
  76         Pattern stats_pattern = Pattern.compile(stats);
  77         output.shouldMatch("Ext Root Scanning \\(ms\\):");
  78         // Find the printed average from the log.
  79         String std_out = output.getStdout();
  80         String avg_line = "(Avg: +)(\\d+\\.\\d+)(,)";
  81         Matcher m = Pattern.compile(avg_line, Pattern.MULTILINE).matcher(std_out);
  82         if (!m.find()) {
  83             throw new Exception("Could not find Avg: dd.d in stdout\n," + output.getStdout());
  84         }
  85         String value_string = m.group(2);
  86         // Count the decimal places in the printed average.  The
  87         // number of decimal places will be used to calculate the
  88         // rounding error when the per thread values are summed in
  89         // this test.
  90         DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
  91         char decimal_symbol = symbols.getDecimalSeparator();
  92         String decimal_places_string = value_string.substring(value_string.indexOf(decimal_symbol) + 1);
  93         int decimal_points = decimal_places_string.length();
  94         double avg_value = 0.0;
  95         try {
  96             avg_value = Double.parseDouble(value_string);
  97         } catch (java.lang.NumberFormatException e) {
  98             System.out.println("Non Number Format - " + value_string);
  99         }
 100         // Go to the next line which will have a "GC" in it.  For
 101         // example:
 102         //     [0.234s][trace  ][gc,phases            ] GC(0)                                  1.58  0.58 
 103         Pattern GC_pattern = Pattern.compile("GC");
 104         m.usePattern(GC_pattern);
 105         if (m.find()) {
 106             String[] per_thread_values =  std_out.substring(m.end()).split(System.lineSeparator(), 2);
 107             String value_regex = " +\\d+\\.\\d+";
 108             Matcher value_m = Pattern.compile(value_regex, Pattern.MULTILINE).matcher(per_thread_values[0]);
 109             int workers = 0;
 110             Double sum = 0.0;
 111             // Count and sum the values.  Note that each parsed value has rounding error in it.
 112             while (value_m.find()) {
 113                 try {
 114                     Double value = Double.parseDouble(value_m.group());
 115                     sum += value;
 116                     workers++;
 117                 } catch (java.lang.NumberFormatException e) {
 118                     System.out.println("Non Number Format - " + m.group());
 119                 }
 120             }
 121             if (workers > 0) {
 122                 Double calculated_avg = sum / workers;
 123                 // Rounding error in per thread data
 124                 Double rounding_error = workers * 0.5 * Math.pow(10.0,  -decimal_points);
 125                 if (Math.abs(calculated_avg - avg_value) > rounding_error) {
 126                   throw new Exception("Average from log " + avg_value + " and average calculated by test " + 
 127                     calculated_avg + " can differ by rounding error " + rounding_error);
 128                 } else {
 129                     System.err.println("PASSED: Average from log " + avg_value + " and average calculated by test " + 
 130                     calculated_avg + " can differ by rounding error " + rounding_error);
 131                 }
 132             }
 133         }
 134     }
 135 
 136     public static void main(String[] args) throws Exception {
 137         runTest();
 138     }
 139 
 140     static class GCTest {
 141         public static void main(String [] args) {
 142             int numGCs = 4;
 143 
 144             // Perform the requested amount of GCs.
 145             WhiteBox wb = WhiteBox.getWhiteBox();
 146             for (int i = 0; i < numGCs - 1; i++) {
 147                 wb.youngGC();
 148             }
 149             if (numGCs > 0) {
 150               wb.fullGC();
 151             }
 152             System.out.println("Done");
 153         }
 154     }
 155 }
 156