test/lib/jdk/test/lib/jfr/GCHelper.java
changeset 50113 caf115bb98ad
child 54956 43340a79840d
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package jdk.test.lib.jfr;
       
    26 
       
    27 import static jdk.test.lib.Asserts.assertEquals;
       
    28 import static jdk.test.lib.Asserts.assertNotEquals;
       
    29 import static jdk.test.lib.Asserts.assertNotNull;
       
    30 import static jdk.test.lib.Asserts.assertNull;
       
    31 import static jdk.test.lib.Asserts.fail;
       
    32 
       
    33 import java.io.FileOutputStream;
       
    34 import java.io.IOException;
       
    35 import java.io.PrintStream;
       
    36 import java.lang.management.GarbageCollectorMXBean;
       
    37 import java.lang.management.ManagementFactory;
       
    38 import java.time.Instant;
       
    39 import java.util.ArrayList;
       
    40 import java.util.Arrays;
       
    41 import java.util.Collections;
       
    42 import java.util.HashMap;
       
    43 import java.util.HashSet;
       
    44 import java.util.List;
       
    45 import java.util.Map;
       
    46 import java.util.Set;
       
    47 import java.util.Stack;
       
    48 
       
    49 import jdk.jfr.ValueDescriptor;
       
    50 import jdk.jfr.consumer.RecordedEvent;
       
    51 
       
    52 /**
       
    53  * Mixed helper classes to test GC events.
       
    54  */
       
    55 public class GCHelper {
       
    56     public static final String event_garbage_collection = EventNames.GarbageCollection;
       
    57     public static final String event_young_garbage_collection = EventNames.YoungGarbageCollection;
       
    58     public static final String event_old_garbage_collection = EventNames.OldGarbageCollection;
       
    59     public static final String event_parold_garbage_collection = EventNames.ParallelOldCollection;
       
    60     public static final String event_g1_garbage_collection = EventNames.G1GarbageCollection;
       
    61     public static final String event_heap_summary = EventNames.GCHeapSummary;
       
    62     public static final String event_heap_ps_summary = EventNames.PSHeapSummary;
       
    63     public static final String event_heap_metaspace_summary = EventNames.MetaspaceSummary;
       
    64     public static final String event_reference_statistics = EventNames.GCReferenceStatistics;
       
    65     public static final String event_phases_pause = EventNames.GCPhasePause;
       
    66     public static final String event_phases_level_1 = EventNames.GCPhasePauseLevel1;
       
    67     public static final String event_phases_level_2 = EventNames.GCPhasePauseLevel2;
       
    68     public static final String event_phases_level_3 = EventNames.GCPhasePauseLevel3;
       
    69 
       
    70     public static final String gcG1New = "G1New";
       
    71     public static final String gcParNew = "ParNew";
       
    72     public static final String gcDefNew = "DefNew";
       
    73     public static final String gcParallelScavenge = "ParallelScavenge";
       
    74     public static final String gcG1Old = "G1Old";
       
    75     public static final String gcG1Full = "G1Full";
       
    76     public static final String gcConcurrentMarkSweep = "ConcurrentMarkSweep";
       
    77     public static final String gcSerialOld = "SerialOld";
       
    78     public static final String gcPSMarkSweep = "PSMarkSweep";
       
    79     public static final String gcParallelOld = "ParallelOld";
       
    80     public static final String pauseLevelEvent = "GCPhasePauseLevel";
       
    81 
       
    82     private static final List<String> g1HeapRegionTypes;
       
    83     private static PrintStream defaultErrorLog = null;
       
    84 
       
    85     public static int getGcId(RecordedEvent event) {
       
    86         return Events.assertField(event, "gcId").getValue();
       
    87     }
       
    88 
       
    89     public static boolean isGcEvent(RecordedEvent event) {
       
    90         for (ValueDescriptor v : event.getFields()) {
       
    91             if ("gcId".equals(v.getName())) {
       
    92                 return true;
       
    93             }
       
    94         }
       
    95         return false;
       
    96     }
       
    97 
       
    98 //    public static String getEventDesc(RecordedEvent event) {
       
    99 //      final String path = event.getEventType().getName();
       
   100 //        if (!isGcEvent(event)) {
       
   101 //            return path;
       
   102 //        }
       
   103 //        if (event_garbage_collection.equals(path)) {
       
   104 //            String name = Events.assertField(event, "name").getValue();
       
   105 //            String cause = Events.assertField(event, "cause").getValue();
       
   106 //            return String.format("path=%s, gcId=%d, endTime=%d, name=%s, cause=%s, startTime=%d",
       
   107 //                    path, getGcId(event), event.getEndTime(), name, cause, event.getStartTime());
       
   108 //        } else {
       
   109 //            return String.format("path=%s, gcId=%d, endTime=%d", path, getGcId(event), event.getEndTime());
       
   110 //        }
       
   111 //    }
       
   112 
       
   113     public static RecordedEvent getConfigEvent(List<RecordedEvent> events) throws Exception {
       
   114         for (RecordedEvent event : events) {
       
   115             if (EventNames.GCConfiguration.equals(event.getEventType().getName())) {
       
   116                 return event;
       
   117             }
       
   118         }
       
   119         fail("Could not find event " + EventNames.GCConfiguration);
       
   120         return null;
       
   121     }
       
   122 
       
   123     public static void callSystemGc(int num, boolean withGarbage) {
       
   124         for (int i = 0; i < num; i++) {
       
   125             if (withGarbage) {
       
   126                 makeGarbage();
       
   127             }
       
   128             System.gc();
       
   129         }
       
   130     }
       
   131 
       
   132     private static void makeGarbage() {
       
   133         Object[] garbage = new Object[1024];
       
   134         for (int i = 0; i < 1024; i++) {
       
   135             garbage[i] = new Object();
       
   136         }
       
   137     }
       
   138 
       
   139     // Removes gcEvents with lowest and highest gcID. This is used to filter out
       
   140     // any incomplete GCs if the recording started/stopped in the middle of a GC.
       
   141     // We also filters out events without gcId. Those events are not needed.
       
   142     public static List<RecordedEvent> removeFirstAndLastGC(List<RecordedEvent> events) {
       
   143         int minGcId = Integer.MAX_VALUE;
       
   144         int maxGcId = Integer.MIN_VALUE;
       
   145         // Find min/max gcId
       
   146         for (RecordedEvent event : events) {
       
   147             if (Events.hasField(event, "gcId")) {
       
   148                 int gcId = Events.assertField(event, "gcId").getValue();
       
   149                 minGcId = Math.min(gcId, minGcId);
       
   150                 maxGcId = Math.max(gcId, maxGcId);
       
   151             }
       
   152         }
       
   153 
       
   154         // Add all events except those with gcId = min/max gcId
       
   155         List<RecordedEvent> filteredEvents = new ArrayList<>();
       
   156         for (RecordedEvent event : events) {
       
   157             if (Events.hasField(event, "gcId")) {
       
   158                 int gcId = Events.assertField(event, "gcId").getValue();
       
   159                 if (gcId != minGcId && gcId != maxGcId) {
       
   160                     filteredEvents.add(event);
       
   161                 }
       
   162             }
       
   163         }
       
   164         return filteredEvents;
       
   165     }
       
   166 
       
   167     public static Map<String, Boolean> beanCollectorTypes = new HashMap<>();
       
   168     public static Set<String> collectorOverrides = new HashSet<>();
       
   169     public static Map<String, String[]> requiredEvents = new HashMap<>();
       
   170 
       
   171     static {
       
   172         // young GarbageCollectionMXBeans.
       
   173         beanCollectorTypes.put("G1 Young Generation", true);
       
   174         beanCollectorTypes.put("Copy", true);
       
   175         beanCollectorTypes.put("PS Scavenge", true);
       
   176         beanCollectorTypes.put("ParNew", true);
       
   177 
       
   178         // old GarbageCollectionMXBeans.
       
   179         beanCollectorTypes.put("G1 Old Generation", false);
       
   180         beanCollectorTypes.put("ConcurrentMarkSweep", false);
       
   181         beanCollectorTypes.put("PS MarkSweep", false);
       
   182         beanCollectorTypes.put("MarkSweepCompact", false);
       
   183 
       
   184         // List of expected collector overrides. "A.B" means that collector A may use collector B.
       
   185         collectorOverrides.add("G1Old.G1Full");
       
   186         collectorOverrides.add("ConcurrentMarkSweep.SerialOld");
       
   187         collectorOverrides.add("SerialOld.PSMarkSweep");
       
   188 
       
   189         requiredEvents.put(gcG1New, new String[] {event_heap_summary, event_young_garbage_collection});
       
   190         requiredEvents.put(gcParNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
       
   191         requiredEvents.put(gcDefNew, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
       
   192         requiredEvents.put(gcParallelScavenge, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_young_garbage_collection});
       
   193         requiredEvents.put(gcG1Old, new String[] {event_heap_summary, event_old_garbage_collection});
       
   194         requiredEvents.put(gcG1Full, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection});
       
   195         requiredEvents.put(gcConcurrentMarkSweep, new String[] {event_phases_pause, event_phases_level_1, event_old_garbage_collection});
       
   196         requiredEvents.put(gcSerialOld, new String[] {event_heap_summary, event_heap_metaspace_summary, event_phases_pause, event_phases_level_1, event_old_garbage_collection});
       
   197         requiredEvents.put(gcParallelOld, new String[] {event_heap_summary, event_heap_ps_summary, event_heap_metaspace_summary, event_reference_statistics, event_phases_pause, event_phases_level_1, event_old_garbage_collection, event_parold_garbage_collection});
       
   198 
       
   199         String[] g1HeapRegionTypeLiterals = new String[] {
       
   200                                                            "Free",
       
   201                                                            "Eden",
       
   202                                                            "Survivor",
       
   203                                                            "Starts Humongous",
       
   204                                                            "Continues Humongous",
       
   205                                                            "Old",
       
   206                                                            "Archive"
       
   207                                                          };
       
   208 
       
   209         g1HeapRegionTypes = Collections.unmodifiableList(Arrays.asList(g1HeapRegionTypeLiterals));
       
   210     }
       
   211 
       
   212     /**
       
   213      * Contains all GC events belonging to the same GC (same gcId).
       
   214      */
       
   215     public static class GcBatch {
       
   216         private List<RecordedEvent> events = new ArrayList<>();
       
   217 
       
   218         public int getGcId() {
       
   219             if (events.isEmpty()) {
       
   220                 return -1;
       
   221             }
       
   222             return GCHelper.getGcId(events.get(0));
       
   223         }
       
   224 
       
   225         public String getName() {
       
   226             RecordedEvent endEvent = getEndEvent();
       
   227             String name = endEvent == null ? null : Events.assertField(endEvent, "name").getValue();
       
   228             return name == null ? "null" : name;
       
   229         }
       
   230 
       
   231         public RecordedEvent getEndEvent() {
       
   232             return getEvent(event_garbage_collection);
       
   233         }
       
   234 
       
   235         public boolean addEvent(RecordedEvent event) {
       
   236             if (!events.isEmpty()) {
       
   237                 assertEquals(getGcId(), GCHelper.getGcId(event), "Wrong gcId in event. Error in test code.");
       
   238             }
       
   239             boolean isEndEvent = event_garbage_collection.equals(event.getEventType().getName());
       
   240             if (isEndEvent) {
       
   241                 // Verify that we have not already got a garbage_collection event with this gcId.
       
   242                 assertNull(getEndEvent(), String.format("Multiple %s for gcId %d", event_garbage_collection, getGcId()));
       
   243             }
       
   244             events.add(event);
       
   245             return isEndEvent;
       
   246         }
       
   247 
       
   248         public boolean isYoungCollection() {
       
   249             boolean isYoung = containsEvent(event_young_garbage_collection);
       
   250             boolean isOld = containsEvent(event_old_garbage_collection);
       
   251             assertNotEquals(isYoung, isOld, "isYoung and isOld was same for batch: " + toString());
       
   252             return isYoung;
       
   253         }
       
   254 
       
   255         public int getEventCount() {
       
   256             return events.size();
       
   257         }
       
   258 
       
   259         public RecordedEvent getEvent(int index) {
       
   260             return events.get(index);
       
   261         }
       
   262 
       
   263         public List<RecordedEvent> getEvents() {
       
   264             return events;
       
   265         }
       
   266 
       
   267         public RecordedEvent getEvent(String eventPath) {
       
   268             for (RecordedEvent event : events) {
       
   269                 if (eventPath.equals(event.getEventType().getName())) {
       
   270                     return event;
       
   271                 }
       
   272             }
       
   273             return null;
       
   274         }
       
   275 
       
   276         public boolean containsEvent(String eventPath) {
       
   277             return getEvent(eventPath) != null;
       
   278         }
       
   279 
       
   280         public String toString() {
       
   281             RecordedEvent endEvent = getEndEvent();
       
   282             Instant startTime = Instant.EPOCH;
       
   283             String cause = "?";
       
   284             String name = "?";
       
   285             if (endEvent != null) {
       
   286                 name = getName();
       
   287                 startTime = endEvent.getStartTime();
       
   288                 cause = Events.assertField(endEvent, "cause").getValue();
       
   289             }
       
   290             return String.format("GcEvent: gcId=%d, method=%s, cause=%s, startTime=%s",
       
   291                     getGcId(), name, cause, startTime);
       
   292         }
       
   293 
       
   294         public String getLog() {
       
   295             StringBuilder sb = new StringBuilder();
       
   296             sb.append(this.toString() + System.getProperty("line.separator"));
       
   297             for (RecordedEvent event : events) {
       
   298                 sb.append(String.format("event: %s%n", event));
       
   299             }
       
   300             return sb.toString();
       
   301         }
       
   302 
       
   303         // Group all events info batches.
       
   304         public static List<GcBatch> createFromEvents(List<RecordedEvent> events) throws Exception {
       
   305             Stack<Integer> openGcIds = new Stack<>();
       
   306             List<GcBatch> batches = new ArrayList<>();
       
   307             GcBatch currBatch = null;
       
   308 
       
   309             for (RecordedEvent event : events) {
       
   310                 if (!isGcEvent(event)) {
       
   311                     continue;
       
   312                 }
       
   313                 int gcId = GCHelper.getGcId(event);
       
   314                 if (currBatch == null || currBatch.getGcId() != gcId) {
       
   315                     currBatch = null;
       
   316                     // Search for existing batch
       
   317                     for (GcBatch loopBatch : batches) {
       
   318                         if (gcId == loopBatch.getGcId()) {
       
   319                             currBatch = loopBatch;
       
   320                             break;
       
   321                         }
       
   322                     }
       
   323                     if (currBatch == null) {
       
   324                         // No existing batch. Create new.
       
   325                         currBatch = new GcBatch();
       
   326                         batches.add(currBatch);
       
   327                         openGcIds.push(new Integer(gcId));
       
   328                     }
       
   329                 }
       
   330                 boolean isEndEvent = currBatch.addEvent(event);
       
   331                 if (isEndEvent) {
       
   332                     openGcIds.pop();
       
   333                 }
       
   334             }
       
   335             // Verify that all start_garbage_collection events have received a corresponding "garbage_collection" event.
       
   336             for (GcBatch batch : batches) {
       
   337                 if (batch.getEndEvent() == null) {
       
   338                     System.out.println(batch.getLog());
       
   339                 }
       
   340                 assertNotNull(batch.getEndEvent(), "GcBatch has no end event");
       
   341             }
       
   342             return batches;
       
   343         }
       
   344     }
       
   345 
       
   346     /**
       
   347      * Contains number of collections and sum pause time for young and old collections.
       
   348      */
       
   349     public static class CollectionSummary {
       
   350         public long collectionCountOld;
       
   351         public long collectionCountYoung;
       
   352         public long collectionTimeOld;
       
   353         public long collectionTimeYoung;
       
   354         private Set<String> names = new HashSet<>();
       
   355 
       
   356         public void add(String collectorName, boolean isYoung, long count, long time) {
       
   357             if (isYoung) {
       
   358                 collectionCountYoung += count;
       
   359                 collectionTimeYoung += time;
       
   360             } else {
       
   361                 collectionCountOld += count;
       
   362                 collectionTimeOld += time;
       
   363             }
       
   364             if (!names.contains(collectorName)) {
       
   365                 names.add(collectorName);
       
   366             }
       
   367         }
       
   368 
       
   369         public long sum() {
       
   370             return collectionCountOld + collectionCountYoung;
       
   371         }
       
   372 
       
   373         public CollectionSummary calcDelta(CollectionSummary prev) {
       
   374             CollectionSummary delta = new CollectionSummary();
       
   375             delta.collectionCountOld = this.collectionCountOld - prev.collectionCountOld;
       
   376             delta.collectionTimeOld = this.collectionTimeOld - prev.collectionTimeOld;
       
   377             delta.collectionCountYoung = this.collectionCountYoung - prev.collectionCountYoung;
       
   378             delta.collectionTimeYoung = this.collectionTimeYoung - prev.collectionTimeYoung;
       
   379             delta.names.addAll(this.names);
       
   380             delta.names.addAll(prev.names);
       
   381             return delta;
       
   382         }
       
   383 
       
   384         public static CollectionSummary createFromMxBeans() {
       
   385             CollectionSummary summary = new CollectionSummary();
       
   386             List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
       
   387             for (int c=0; c<gcBeans.size(); c++) {
       
   388                 GarbageCollectorMXBean currBean = gcBeans.get(c);
       
   389                 Boolean isYoung = beanCollectorTypes.get(currBean.getName());
       
   390                 assertNotNull(isYoung, "Unknown MXBean name: " + currBean.getName());
       
   391                 long collectionTime = currBean.getCollectionTime() * 1000; // Convert from millis to micros.
       
   392                 summary.add(currBean.getName(), isYoung.booleanValue(), currBean.getCollectionCount(), collectionTime);
       
   393             }
       
   394             return summary;
       
   395         }
       
   396 
       
   397         public static CollectionSummary createFromEvents(List<GcBatch> batches) {
       
   398             CollectionSummary summary = new CollectionSummary();
       
   399             for (GcBatch batch : batches) {
       
   400                 RecordedEvent endEvent = batch.getEndEvent();
       
   401                 assertNotNull(endEvent, "No end event in batch with gcId " + batch.getGcId());
       
   402                 String name = batch.getName();
       
   403                 summary.add(name, batch.isYoungCollection(), 1, Events.assertField(endEvent, "sumOfPauses").getValue());
       
   404             }
       
   405             return summary;
       
   406         }
       
   407 
       
   408         public String toString() {
       
   409             StringBuilder collectorNames = new StringBuilder();
       
   410             for (String s : names) {
       
   411                 if (collectorNames.length() > 0) {
       
   412                     collectorNames.append(", ");
       
   413                 }
       
   414                 collectorNames.append(s);
       
   415             }
       
   416             return String.format("CollectionSummary: young.collections=%d, young.time=%d, old.collections=%d, old.time=%d, collectors=(%s)",
       
   417                     collectionCountYoung, collectionTimeYoung, collectionCountOld, collectionTimeOld, collectorNames);
       
   418         }
       
   419     }
       
   420 
       
   421     public static PrintStream getDefaultErrorLog() {
       
   422         if (defaultErrorLog == null) {
       
   423             try {
       
   424                 defaultErrorLog = new PrintStream(new FileOutputStream("error.log", true));
       
   425             } catch (IOException e) {
       
   426                 e.printStackTrace();
       
   427                 defaultErrorLog = System.err;
       
   428             }
       
   429         }
       
   430         return defaultErrorLog;
       
   431     }
       
   432 
       
   433     public static void log(Object msg) {
       
   434         log(msg, System.err);
       
   435         log(msg, getDefaultErrorLog());
       
   436     }
       
   437 
       
   438     public static void log(Object msg, PrintStream ps) {
       
   439         ps.println(msg);
       
   440     }
       
   441 
       
   442     public static boolean isValidG1HeapRegionType(final String type) {
       
   443         return g1HeapRegionTypes.contains(type);
       
   444     }
       
   445 
       
   446     /**
       
   447      * Helper function to align heap size up.
       
   448      *
       
   449      * @param value
       
   450      * @param alignment
       
   451      * @return aligned value
       
   452      */
       
   453     public static long alignUp(long value, long alignment) {
       
   454         return (value + alignment - 1) & ~(alignment - 1);
       
   455     }
       
   456 
       
   457     /**
       
   458      * Helper function to align heap size down.
       
   459      *
       
   460      * @param value
       
   461      * @param alignment
       
   462      * @return aligned value
       
   463      */
       
   464     public static long alignDown(long value, long alignment) {
       
   465         return value & ~(alignment - 1);
       
   466     }
       
   467 }