1 /*
2  * Copyright (c) 2013, 2018, 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 import java.util.Arrays;
25 
26 import jdk.testlibrary.Utils;
27 import static jdk.testlibrary.Asserts.*;
28 import java.text.NumberFormat;
29 
30 /**
31  * The helper class for parsing following output from command 'jstat -gcutil':
32  *
33  *  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT
34  *  0.00   0.00  86.67   0.00   -      -      0      0.000     0    0.000     0    0.000    0.000
35  *  0.00   0.00  86.67   0.00   -      -      0      0.000     0    0.000     0    0.000    0.000
36  *
37  *  It will be verified that numerical values have defined types and are reasonable,
38  *  for example percentage should fit within 0-100 interval.
39  */
40 public class JstatGCUtilParser {
41 
42     public enum GcStatisticsType {
43         INTEGER, DOUBLE, PERCENTAGE, PERCENTAGE_OR_DASH;
44     }
45 
46     public enum GcStatistics {
47         S0(GcStatisticsType.PERCENTAGE),
48         S1(GcStatisticsType.PERCENTAGE),
49         E(GcStatisticsType.PERCENTAGE),
50         O(GcStatisticsType.PERCENTAGE),
51         M(GcStatisticsType.PERCENTAGE_OR_DASH),
52         CCS(GcStatisticsType.PERCENTAGE_OR_DASH),
53         YGC(GcStatisticsType.INTEGER),
54         YGCT(GcStatisticsType.DOUBLE),
55         FGC(GcStatisticsType.INTEGER),
56         FGCT(GcStatisticsType.DOUBLE),
57         CGC(GcStatisticsType.INTEGER),
58         CGCT(GcStatisticsType.DOUBLE),
59         GCT(GcStatisticsType.DOUBLE);
60 
61         private final GcStatisticsType type;
62 
GcStatistics(GcStatisticsType type)63         private GcStatistics(GcStatisticsType type) {
64             this.type = type;
65         }
66 
getType()67         private GcStatisticsType getType() {
68             return type;
69         }
70 
isHeadline(String... valueArray)71         public static boolean isHeadline(String... valueArray) {
72             if (valueArray.length != values().length) {
73                 return false;
74             }
75             int headersCount = 0;
76             for (int i = 0; i < values().length; i++) {
77                 if (valueArray[i].equals(values()[i].toString())) {
78                     headersCount++;
79                 }
80             }
81             if (headersCount != values().length) {
82                 return false;
83             }
84             return true;
85         }
86 
verifyLength(String... valueArray)87         private static void verifyLength(String... valueArray) throws Exception {
88             assertEquals(valueArray.length, values().length,
89                     "Invalid number of data columns: " + Arrays.toString(valueArray));
90         }
91 
verify(String... valueArray)92         public static void verify(String... valueArray) throws Exception {
93             verifyLength(valueArray);
94             for (int i = 0; i < values().length; i++) {
95                 GcStatisticsType type = values()[i].getType();
96                 String value = valueArray[i].trim();
97                 if (type.equals(GcStatisticsType.INTEGER)) {
98                     NumberFormat.getInstance().parse(value).intValue();
99                     break;
100                 }
101                 if (type.equals(GcStatisticsType.DOUBLE)) {
102                     NumberFormat.getInstance().parse(value).doubleValue();
103                     break;
104                 }
105                 if (type.equals(GcStatisticsType.PERCENTAGE_OR_DASH) &&
106                         value.equals("-")) {
107                     break;
108                 }
109                 double percentage = NumberFormat.getInstance().parse(value).doubleValue();
110                 assertTrue(0 <= percentage && percentage <= 100,
111                         "Not a percentage: " + value);
112             }
113         }
114 
115     }
116 
117     private final String output;
118 
JstatGCUtilParser(String output)119     public JstatGCUtilParser(String output) {
120         this.output = output;
121     }
122 
getOutput()123     public String getOutput() {
124         return output;
125     }
126 
127     /**
128      * The function will discard any lines that come before the header line.
129      * This can happen if the JVM outputs a warning message for some reason
130      * before running jstat.
131      */
parse(int samples)132     public void parse(int samples) throws Exception {
133         boolean headlineFound = false;
134         int datalineCount = 0;
135 
136         String[] lines = output.split(Utils.NEW_LINE);
137         for (String line : lines) {
138             line = line.replaceAll("\\s+", " ").trim();
139             String[] valueArray = line.split(" ");
140 
141             if (!headlineFound) {
142                 headlineFound = GcStatistics.isHeadline(valueArray);
143                 continue;
144             }
145 
146             GcStatistics.verify(valueArray);
147             datalineCount++;
148         }
149 
150         assertTrue(headlineFound, "No or invalid headline found, expected: " +
151                 Utils.NEW_LINE + Arrays.toString(GcStatistics.values()).replaceAll(",", " "));
152         assertEquals(samples, datalineCount,
153                 "Expected " + samples + " samples, got " + datalineCount);
154     }
155 
156 }
157