43 * Batch contains expected events depending on garbage_collection.name
44 *
45 * garbage_collection_start.timestamp == garbage_collection.startTime.
46 *
47 * garbage_collection.timestamp >= timestamp for all other events in batch.
48 *
49 * The start_garbage_collection and garbage_collection events must be synchronized.
50 * This means that there may be multiple start_garbage_collection before a garbage_collection,
51 * but garbage_collection.gcId must be equal to latest start_garbage_collection.gcId.
52 *
53 * start_garbage_collection must be the first event in the batch,
54 * that means no event with same gcId before garbage_collection_start event.
55 *
56 * garbage_collection.name matches what is expected by the collectors specified in initial_configuration.
57 *
58 * Duration for event "vm/gc/phases/pause" >= 1. Duration for phase level events >= 0.
59 *
60 *
61 */
62 public class GCEventAll {
63 private static Set<Long> gcIds = new HashSet<Long>();
64 private static int systemGcCount = 0;
65 private boolean isIncompleteBatchRemoved = false;
66
67 private String youngCollector = null;
68 private String oldCollector = null;
69
70 /**
71 * Runs the test once with given worker threads.
72 * @param workerThreads Threads that generates GCs.
73 * @param gcIds Set of all used gcIds
74 * @throws Exception
75 */
76 public void doSingleTest(Thread[] workerThreads) throws Throwable {
77 TestRecording r = new TestRecording();
78 final String gcEventRoot = "vm/gc/*";
79 List<FLREvent> events = null;
80 try {
81 r.createJVMSetting(gcEventRoot, true, false, 0, 0);
82
83 // Start with a full GC to minimize risk of getting extra GC between
205 }
206 }
207
208 /**
209 * When using collector ConcurrentMarkSweep with -XX:+ExplicitGCInvokesConcurrent, the JFR recording may
210 * stop before we have received the last garbage_collection event.
211 *
212 * This function does 3 things:
213 * 1. Check if the last batch is incomplete.
214 * 2. If it is incomplete, then asserts that incomplete batches are allowed for this configuration.
215 * 3. If incomplete batches are allowed, then the incomplete batch is removed.
216 *
217 * @param events All events
218 * @return All events with any incomplete batch removed.
219 * @throws Throwable
220 */
221 private List<FLREvent> filterIncompleteGcBatch(List<FLREvent> events) throws Throwable {
222 List<FLREvent> returnEvents = new ArrayList<FLREvent>();
223 returnEvents.addAll(events);
224
225 long lastGcId = getLastGcId(events);
226 List<FLREvent> lastBatchEvents = getEventsWithGcId(events, lastGcId);
227 String[] endEvents = {GCHelper.event_garbage_collection, GCHelper.event_old_garbage_collection, GCHelper.event_young_garbage_collection};
228 boolean isComplete = containsAnyPath(lastBatchEvents, endEvents);
229 if (!isComplete) {
230 // The last GC batch does not contain an end event. The batch is incomplete.
231 // This is only allowed if we are using old_collector="ConcurrentMarkSweep" and "-XX:+ExplicitGCInvokesConcurrent"
232 boolean isExplicitGCInvokesConcurrent = hasInputArgument("-XX:+ExplicitGCInvokesConcurrent");
233 boolean isConcurrentMarkSweep = GCHelper.gcConcurrentMarkSweep.equals(oldCollector);
234 String msg = String.format(
235 "Incomplete batch only allowed for '%s' with -XX:+ExplicitGCInvokesConcurrent",
236 GCHelper.gcConcurrentMarkSweep);
237 Asserts.assertTrue(isConcurrentMarkSweep && isExplicitGCInvokesConcurrent, msg);
238
239 // This batch is incomplete, but that is allowed with the current settings.
240 // Remove the incomplete batch.
241 returnEvents.removeAll(lastBatchEvents);
242 }
243 return returnEvents;
244 }
245
248 * @param arg The argument to search for
249 * @return true if the argument is on the command line.
250 */
251 private boolean hasInputArgument(String arg) {
252 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
253 List<String> arguments = runtimeMxBean.getInputArguments();
254 for (String currArg : arguments) {
255 if (currArg.equals(arg)) {
256 return true;
257 }
258 }
259 return false;
260 }
261
262 /**
263 * Get all events that has the specified gcId.
264 * @param events All events.
265 * @param gcId The specified gcId.
266 * @return A list of the events with the specified gcId.
267 */
268 private List<FLREvent> getEventsWithGcId(List<FLREvent> events, long gcId) {
269 List<FLREvent> batchEvents = new ArrayList<FLREvent>();
270 for (FLREvent event : events) {
271 if (GCHelper.isGcEvent(event) && GCHelper.getGcId(event) == gcId) {
272 batchEvents.add(event);
273 }
274 }
275 return batchEvents;
276 }
277
278 /**
279 * Checks if at least one event in the list has one of the specified paths.
280 * @param events All events.
281 * @param paths The paths to search for.
282 * @return true if at least one of the events has one of the specified paths.
283 */
284 private boolean containsAnyPath(List<FLREvent> events, String[] paths) {
285 Set<String> pathSet = new HashSet<String>(Arrays.asList(paths));
286 for (FLREvent event : events) {
287 if (pathSet.contains(event.getPath())) {
288 return true;
289 }
290 }
291 return false;
292 }
293
294 /**
295 * Return the highest gcId in the list.
296 * @param events All events.
297 * @return the highest gcId found in the list or -1 if no event had a gcId.
298 */
299 private long getLastGcId(List<FLREvent> events) {
300 long lastGcId = -1;
301 for (FLREvent event : events) {
302 if (GCHelper.isGcEvent(event)) {
303 long gcId = GCHelper.getGcId(event);
304 if (gcId > lastGcId) {
305 lastGcId = gcId;
306 }
307 }
308 }
309 Asserts.assertTrue(lastGcId != -1, "No gcId found");
310 return lastGcId;
311 }
312
313 /**
314 * Verifies collection count reported by flight recorder events against the values
315 * reported by GarbageCollectionMXBean.
316 * Number of collections should match exactly.
317 * Sum pause time are allowed some margin of error because of rounding errors in measurements.
318 */
319 private void verifyCollectionCount(GCHelper.CollectionSummary eventCounts, GCHelper.CollectionSummary beanCounts) {
320 verifyCollectionCount(youngCollector, eventCounts.collectionCountYoung, beanCounts.collectionCountYoung);
321 verifyCollectionCount(oldCollector, eventCounts.collectionCountOld, beanCounts.collectionCountOld);
322 }
323
383 countAfterGc++;
384 } else {
385 Asserts.assertTrue(false, "Unknown value for heap_summary.when: '" + when + "'");
386 }
387 }
388 }
389 if (!GCHelper.gcConcurrentMarkSweep.equals(batch.getName())) {
390 // We do not get heap_summary events for ConcurrentMarkSweep
391 Asserts.assertEquals(1, countBeforeGc, "Unexpected number of heap_summary.before_gc");
392 Asserts.assertEquals(1, countAfterGc, "Unexpected number of heap_summary.after_gc");
393 }
394 } catch (Throwable e) {
395 throw e;
396 }
397 }
398 }
399
400 /**
401 * Verify that all gcId are unique.
402 */
403 private void verifyUniqueIds(List<GCHelper.GcBatch> batches, Set<Long> gcIds) {
404 for (GCHelper.GcBatch batch : batches) {
405 Long gcId = new Long(batch.getGcId());
406 Asserts.assertFalse(gcIds.contains(gcId), "Duplicate gcId: " + gcId);
407 gcIds.add(gcId);
408 }
409 }
410
411 /**
412 * Verify phase events.
413 */
414 private void verifyPhaseEvents(List<GCHelper.GcBatch> batches) {
415 for (GCHelper.GcBatch batch : batches) {
416 for(FLREvent event : batch.getEvents()) {
417 // Only check events that start with "vm/gc/phases/"
418 if (event.getPath().startsWith(GCHelper.event_phases_group)) {
419 long batchStartTime = batch.getEndEvent().getStartTime();
420 Asserts.assertGreaterOrEqualThan(event.getStartTime(), batchStartTime, "Phase startTime >= batch startTime");
421 long minDuration = 0;
422 long duration = event.getTimestamp() - event.getStartTime();
423 Asserts.assertGreaterOrEqualThan(duration, minDuration, "Phase event duration >= " + minDuration);
424 }
425 }
|
43 * Batch contains expected events depending on garbage_collection.name
44 *
45 * garbage_collection_start.timestamp == garbage_collection.startTime.
46 *
47 * garbage_collection.timestamp >= timestamp for all other events in batch.
48 *
49 * The start_garbage_collection and garbage_collection events must be synchronized.
50 * This means that there may be multiple start_garbage_collection before a garbage_collection,
51 * but garbage_collection.gcId must be equal to latest start_garbage_collection.gcId.
52 *
53 * start_garbage_collection must be the first event in the batch,
54 * that means no event with same gcId before garbage_collection_start event.
55 *
56 * garbage_collection.name matches what is expected by the collectors specified in initial_configuration.
57 *
58 * Duration for event "vm/gc/phases/pause" >= 1. Duration for phase level events >= 0.
59 *
60 *
61 */
62 public class GCEventAll {
63 private static Set<Integer> gcIds = new HashSet<Integer>();
64 private static int systemGcCount = 0;
65 private boolean isIncompleteBatchRemoved = false;
66
67 private String youngCollector = null;
68 private String oldCollector = null;
69
70 /**
71 * Runs the test once with given worker threads.
72 * @param workerThreads Threads that generates GCs.
73 * @param gcIds Set of all used gcIds
74 * @throws Exception
75 */
76 public void doSingleTest(Thread[] workerThreads) throws Throwable {
77 TestRecording r = new TestRecording();
78 final String gcEventRoot = "vm/gc/*";
79 List<FLREvent> events = null;
80 try {
81 r.createJVMSetting(gcEventRoot, true, false, 0, 0);
82
83 // Start with a full GC to minimize risk of getting extra GC between
205 }
206 }
207
208 /**
209 * When using collector ConcurrentMarkSweep with -XX:+ExplicitGCInvokesConcurrent, the JFR recording may
210 * stop before we have received the last garbage_collection event.
211 *
212 * This function does 3 things:
213 * 1. Check if the last batch is incomplete.
214 * 2. If it is incomplete, then asserts that incomplete batches are allowed for this configuration.
215 * 3. If incomplete batches are allowed, then the incomplete batch is removed.
216 *
217 * @param events All events
218 * @return All events with any incomplete batch removed.
219 * @throws Throwable
220 */
221 private List<FLREvent> filterIncompleteGcBatch(List<FLREvent> events) throws Throwable {
222 List<FLREvent> returnEvents = new ArrayList<FLREvent>();
223 returnEvents.addAll(events);
224
225 int lastGcId = getLastGcId(events);
226 List<FLREvent> lastBatchEvents = getEventsWithGcId(events, lastGcId);
227 String[] endEvents = {GCHelper.event_garbage_collection, GCHelper.event_old_garbage_collection, GCHelper.event_young_garbage_collection};
228 boolean isComplete = containsAnyPath(lastBatchEvents, endEvents);
229 if (!isComplete) {
230 // The last GC batch does not contain an end event. The batch is incomplete.
231 // This is only allowed if we are using old_collector="ConcurrentMarkSweep" and "-XX:+ExplicitGCInvokesConcurrent"
232 boolean isExplicitGCInvokesConcurrent = hasInputArgument("-XX:+ExplicitGCInvokesConcurrent");
233 boolean isConcurrentMarkSweep = GCHelper.gcConcurrentMarkSweep.equals(oldCollector);
234 String msg = String.format(
235 "Incomplete batch only allowed for '%s' with -XX:+ExplicitGCInvokesConcurrent",
236 GCHelper.gcConcurrentMarkSweep);
237 Asserts.assertTrue(isConcurrentMarkSweep && isExplicitGCInvokesConcurrent, msg);
238
239 // This batch is incomplete, but that is allowed with the current settings.
240 // Remove the incomplete batch.
241 returnEvents.removeAll(lastBatchEvents);
242 }
243 return returnEvents;
244 }
245
248 * @param arg The argument to search for
249 * @return true if the argument is on the command line.
250 */
251 private boolean hasInputArgument(String arg) {
252 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
253 List<String> arguments = runtimeMxBean.getInputArguments();
254 for (String currArg : arguments) {
255 if (currArg.equals(arg)) {
256 return true;
257 }
258 }
259 return false;
260 }
261
262 /**
263 * Get all events that has the specified gcId.
264 * @param events All events.
265 * @param gcId The specified gcId.
266 * @return A list of the events with the specified gcId.
267 */
268 private List<FLREvent> getEventsWithGcId(List<FLREvent> events, int gcId) {
269 List<FLREvent> batchEvents = new ArrayList<FLREvent>();
270 for (FLREvent event : events) {
271 if (GCHelper.isGcEvent(event) && GCHelper.getGcId(event) == gcId) {
272 batchEvents.add(event);
273 }
274 }
275 return batchEvents;
276 }
277
278 /**
279 * Checks if at least one event in the list has one of the specified paths.
280 * @param events All events.
281 * @param paths The paths to search for.
282 * @return true if at least one of the events has one of the specified paths.
283 */
284 private boolean containsAnyPath(List<FLREvent> events, String[] paths) {
285 Set<String> pathSet = new HashSet<String>(Arrays.asList(paths));
286 for (FLREvent event : events) {
287 if (pathSet.contains(event.getPath())) {
288 return true;
289 }
290 }
291 return false;
292 }
293
294 /**
295 * Return the highest gcId in the list.
296 * @param events All events.
297 * @return the highest gcId found in the list or -1 if no event had a gcId.
298 */
299 private int getLastGcId(List<FLREvent> events) {
300 int lastGcId = -1;
301 for (FLREvent event : events) {
302 if (GCHelper.isGcEvent(event)) {
303 int gcId = GCHelper.getGcId(event);
304 if (gcId > lastGcId) {
305 lastGcId = gcId;
306 }
307 }
308 }
309 Asserts.assertTrue(lastGcId != -1, "No gcId found");
310 return lastGcId;
311 }
312
313 /**
314 * Verifies collection count reported by flight recorder events against the values
315 * reported by GarbageCollectionMXBean.
316 * Number of collections should match exactly.
317 * Sum pause time are allowed some margin of error because of rounding errors in measurements.
318 */
319 private void verifyCollectionCount(GCHelper.CollectionSummary eventCounts, GCHelper.CollectionSummary beanCounts) {
320 verifyCollectionCount(youngCollector, eventCounts.collectionCountYoung, beanCounts.collectionCountYoung);
321 verifyCollectionCount(oldCollector, eventCounts.collectionCountOld, beanCounts.collectionCountOld);
322 }
323
383 countAfterGc++;
384 } else {
385 Asserts.assertTrue(false, "Unknown value for heap_summary.when: '" + when + "'");
386 }
387 }
388 }
389 if (!GCHelper.gcConcurrentMarkSweep.equals(batch.getName())) {
390 // We do not get heap_summary events for ConcurrentMarkSweep
391 Asserts.assertEquals(1, countBeforeGc, "Unexpected number of heap_summary.before_gc");
392 Asserts.assertEquals(1, countAfterGc, "Unexpected number of heap_summary.after_gc");
393 }
394 } catch (Throwable e) {
395 throw e;
396 }
397 }
398 }
399
400 /**
401 * Verify that all gcId are unique.
402 */
403 private void verifyUniqueIds(List<GCHelper.GcBatch> batches, Set<Integer> gcIds) {
404 for (GCHelper.GcBatch batch : batches) {
405 Integer gcId = new Integer(batch.getGcId());
406 Asserts.assertFalse(gcIds.contains(gcId), "Duplicate gcId: " + gcId);
407 gcIds.add(gcId);
408 }
409 }
410
411 /**
412 * Verify phase events.
413 */
414 private void verifyPhaseEvents(List<GCHelper.GcBatch> batches) {
415 for (GCHelper.GcBatch batch : batches) {
416 for(FLREvent event : batch.getEvents()) {
417 // Only check events that start with "vm/gc/phases/"
418 if (event.getPath().startsWith(GCHelper.event_phases_group)) {
419 long batchStartTime = batch.getEndEvent().getStartTime();
420 Asserts.assertGreaterOrEqualThan(event.getStartTime(), batchStartTime, "Phase startTime >= batch startTime");
421 long minDuration = 0;
422 long duration = event.getTimestamp() - event.getStartTime();
423 Asserts.assertGreaterOrEqualThan(duration, minDuration, "Phase event duration >= " + minDuration);
424 }
425 }
|