Skip to content

Implement Red Hat's Shenandoah GC parsing #187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.tagtraum.perf.gcviewer.imp;

import com.tagtraum.perf.gcviewer.model.GCResource;
import com.tagtraum.perf.gcviewer.util.LocalisationHelper;

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
Expand All @@ -9,9 +12,6 @@
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

import com.tagtraum.perf.gcviewer.model.GCResource;
import com.tagtraum.perf.gcviewer.util.LocalisationHelper;

/**
*
* Date: Jan 30, 2002
Expand Down Expand Up @@ -202,6 +202,11 @@ else if (s.indexOf("starting collection, threshold allocation reached.") != -1)
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: IBM i5/OS 1.4.2");
return new DataReaderIBMi5OS1_4_2(gcResource, in);
}
// Shenandoah
else if (s.contains("Using Shenandoah")) {
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: Red Hat Shenandoah");
return new DataReaderShenandoah(gcResource, in);
}
return null;
}

Expand Down
175 changes: 175 additions & 0 deletions src/main/java/com/tagtraum/perf/gcviewer/imp/DataReaderShenandoah.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package com.tagtraum.perf.gcviewer.imp;

import com.tagtraum.perf.gcviewer.model.AbstractGCEvent;
import com.tagtraum.perf.gcviewer.model.GCModel;
import com.tagtraum.perf.gcviewer.model.GCResource;
import com.tagtraum.perf.gcviewer.model.ShenandoahGCEvent;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
* Currently only parsing 5 main messages for GCViewer with default decorations.
* Initial mark, concurrent mark, final mark, concurrent evacuation
* <p>
* Example Format
* [0.730s][info][gc,start ] GC(0) Pause Init Mark
* [0.731s][info][gc ] GC(0) Pause Init Mark 1.021ms
* [0.731s][info][gc,start ] GC(0) Concurrent marking
* [0.735s][info][gc ] GC(0) Concurrent marking 74M-&gt;74M(128M) 3.688ms
* [0.735s][info][gc,start ] GC(0) Pause Final Mark
* [0.736s][info][gc ] GC(0) Pause Final Mark 74M-&gt;76M(128M) 0.811ms
* [0.736s][info][gc,start ] GC(0) Concurrent evacuation
* ...
* [29.628s][info][gc ] Cancelling concurrent GC: Allocation Failure
* ... skipping detailed messages as those aren't parsed yet
* [43.948s][info][gc ] GC(831) Pause Full (Allocation Failure) 7943M-&gt;6013M(8192M) 14289.335ms
*/
public class DataReaderShenandoah extends AbstractDataReader {


// Input: [0.693s][info][gc ] GC(0) Pause Init Mark 1.070ms
// Group 1: 0.693
// Group 2: 1.070
// Regex without breaking: ^\[([0-9]+[.,][0-9]+)[^\-]*[ ]([0-9]+[.,][0-9]+)
private static final Pattern PATTERN_WITHOUT_HEAP = Pattern.compile("^\\[([0-9]+[.,][0-9]+)[^\\-]*[ ]([0-9]+[.,][0-9]+)");

// Input: [13.522s][info][gc ] GC(708) Concurrent evacuation 4848M->4855M(4998M) 2.872ms
// Group 1: 13.522
// Group 2: 4848M->4855M(4998M)
// Group 3: 2.872
// Regex without breaking: ^\[([0-9]+[.,][0-9]+).*[ ]([0-9]+[BKMG]\-\>[0-9]+[BKMG]\([0-9]+[BKMG]\)) ([0-9]+[.,][0-9]+)
private static final Pattern PATTERN_WITH_HEAP = Pattern.compile("^\\[([0-9]+[.,][0-9]+)" +
".*[ ]([0-9]+[BKMG]\\-\\>[0-9]+[BKMG]\\([0-9]+[BKMG]\\)) " +
"([0-9]+[.,][0-9]+)");

// Input: 4848M->4855M(4998M)
// Group 1: 4848
// Group 2: 4855
// Group 3: 4998
// Regex without breaking: ([0-9]+)[BKMG]\-\>([0-9]+)[BKMG]\(([0-9]+)[BKMG]\)
private static final Pattern PATTERN_HEAP_CHANGES = Pattern.compile("([0-9]+)[BKMG]->([0-9]+)[BKMG]\\(([0-9]+)[BKMG]\\)");

private static final int NO_HEAP_TIMESTAMP = 1;
private static final int NO_HEAP_DURATION = 2;
private static final int WITH_HEAP_TIMESTAMP = 1;
private static final int WITH_HEAP_MEMORY = 2;
private static final int WITH_HEAP_DURATION = 3;
private static final int HEAP_BEFORE = 1;
private static final int HEAP_AFTER = 2;
private static final int HEAP_CURRENT_TOTAL = 3;

private static final List<String> EXCLUDE_STRINGS = Arrays.asList("Using Shenandoah", "Cancelling concurrent GC",
"[gc,start", "[gc,ergo", "[gc,stringtable", "[gc,init", "[gc,heap", "[pagesize", "[class", "[os", "[startuptime",
"[os,thread", "[gc,heap,exit", "Cancelling concurrent GC: Allocation Failure", "Phase ",
"[gc,stats", "[biasedlocking", "[logging", "[verification", "[modules,startuptime", "[safepoint", "[stacktrace",
"[exceptions", "thrown", "at bci", "for thread", "[module,load", "[module,startuptime");

protected DataReaderShenandoah(GCResource gcResource, InputStream in) throws UnsupportedEncodingException {
super(gcResource, in);
}

@Override
public GCModel read() throws IOException {
getLogger().info("Reading Shenandoah format...");

GCModel model = new GCModel();
model.setFormat(GCModel.Format.RED_HAT_SHENANDOAH_GC);

Stream<String> lines = new BufferedReader(in).lines();
lines.filter(this::lineNotInExcludedStrings).forEach(e -> model.add(parseShenandoahEvent(e)));
return model;
}

private AbstractGCEvent<?> parseShenandoahEvent(String line) {
ShenandoahGCEvent event = new ShenandoahGCEvent();

Matcher noHeapMatcher = PATTERN_WITHOUT_HEAP.matcher(line);
Matcher withHeapMatcher = PATTERN_WITH_HEAP.matcher(line);
if (noHeapMatcher.find()) {
if (line.contains("Init Mark")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_STW_INIT_MARK);
} else if (line.contains("Pause Init Update Refs")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_STW_INIT_UPDATE_REFS);
} else {
getLogger().warning("Failed to match line with no heap info: " + line);
}
event.setPause(Double.parseDouble(noHeapMatcher.group(NO_HEAP_DURATION).replace(",", ".")));
event.setTimestamp(Double.parseDouble(noHeapMatcher.group(NO_HEAP_TIMESTAMP).replace(",", ".")));
} else if (withHeapMatcher.find()) {
// Concurrent events
if (line.contains("Concurrent")) {
if (line.contains("Concurrent marking")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_CONCURRENT_CONC_MARK);
event.setConcurrency(true);
} else if (line.contains("Concurrent evacuation")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_CONCURRENT_CONC_EVAC);
event.setConcurrency(true);
} else if (line.contains("Concurrent update references")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_CONCURRENT_CONC_UPDATE_REFS);
event.setConcurrency(true);
} else if (line.contains("Concurrent reset bitmaps")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_CONCURRENT_CONC_RESET_BITMAPS);
event.setConcurrency(true);
} else if (line.contains("Concurrent precleaning")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_CONCURRENT_PRECLEANING);
event.setConcurrency(true);
}
}
// STW events
else {
if (line.contains("Final Mark")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_STW_FINAL_MARK);
} else if (line.contains("Pause Full (Allocation Failure)")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_STW_ALLOC_FAILURE);
} else if (line.contains("Pause Final Update Refs")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_STW_FINAL_UPDATE_REFS);
} else if (line.contains("Pause Full (System.gc())")) {
setEventTypes(event, AbstractGCEvent.Type.SHEN_STW_SYSTEM_GC);
} else {
getLogger().warning("Failed to match line with heap info: " + line);
}
}
event.setPause(Double.parseDouble(withHeapMatcher.group(WITH_HEAP_DURATION).replace(",", ".")));
event.setTimestamp(Double.parseDouble(withHeapMatcher.group(WITH_HEAP_TIMESTAMP).replace(",", ".")));
addHeapDetailsToEvent(event, withHeapMatcher.group(WITH_HEAP_MEMORY));
} else {
getLogger().warning("Found line that has no match:" + line);
}

return event;
}

/**
* @param event GC event to which the heap change information is added
* @param memoryString Memory changes in format 100M->80M(120M) where 100M-before, 80M-after, 120M-max
*/
private void addHeapDetailsToEvent(ShenandoahGCEvent event, String memoryString) {
Matcher matcher = PATTERN_HEAP_CHANGES.matcher(memoryString);
if (matcher.find()) {
event.setPreUsed(Integer.parseInt(matcher.group(HEAP_BEFORE)));
event.setPostUsed(Integer.parseInt(matcher.group(HEAP_AFTER)));
event.setTotal(Integer.parseInt(matcher.group(HEAP_CURRENT_TOTAL)));
event.setExtendedType(event.getExtendedType());
} else {
getLogger().warning("Failed to find heap details from line: " + memoryString);
}
}

private void setEventTypes(ShenandoahGCEvent event, AbstractGCEvent.Type type) {
event.setType(type);
event.setExtendedType(AbstractGCEvent.ExtendedType.lookup(type));
}

private boolean lineNotInExcludedStrings(String line) {
return EXCLUDE_STRINGS.stream().noneMatch(line::contains);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,22 @@ public String toString() {
public static final Type G1_CONCURRENT_COUNT_END = new Type("GC concurrent-count-end", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC_PAUSE);
public static final Type G1_CONCURRENT_CLEANUP_START = new Type("GC concurrent-cleanup-start", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC);
public static final Type G1_CONCURRENT_CLEANUP_END = new Type("GC concurrent-cleanup-end", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC_PAUSE);


// Shenandoah types of Generation.TENURED since Generation.ALL gets ignored apparently (GCEvent.java line 56)
// STW pause events
public static final Type SHEN_STW_INIT_MARK = new Type("GC Pause Initial Mark (STW pause)", Generation.TENURED, Concurrency.SERIAL, GcPattern.GC_PAUSE);
public static final Type SHEN_STW_FINAL_MARK = new Type("GC Pause Final Mark (STW pause)", Generation.TENURED, Concurrency.SERIAL, GcPattern.GC_PAUSE);
public static final Type SHEN_STW_INIT_UPDATE_REFS = new Type("GC Pause Init Update Refs (STW pause)", Generation.TENURED, Concurrency.SERIAL, GcPattern.GC_PAUSE);
public static final Type SHEN_STW_FINAL_UPDATE_REFS = new Type("GC Pause Final Update Refs", Generation.TENURED, Concurrency.SERIAL, GcPattern.GC_PAUSE);
public static final Type SHEN_STW_ALLOC_FAILURE = new Type("GC Pause Full (Allocation Failure)", Generation.TENURED, Concurrency.SERIAL, GcPattern.GC_PAUSE);
public static final Type SHEN_STW_SYSTEM_GC = new Type("GC Pause Full (System.gc())", Generation.TENURED, Concurrency.SERIAL, GcPattern.GC_PAUSE);
// Concurrent events
public static final Type SHEN_CONCURRENT_CONC_MARK = new Type("GC Concurrent marking", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC);
public static final Type SHEN_CONCURRENT_CONC_EVAC = new Type("GC Concurrent evacuation", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC);
public static final Type SHEN_CONCURRENT_CONC_RESET_BITMAPS = new Type("GC Concurrent reset bitmaps", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC);
public static final Type SHEN_CONCURRENT_CONC_UPDATE_REFS = new Type("GC Concurrent update references", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC);
public static final Type SHEN_CONCURRENT_PRECLEANING = new Type("GC Concurrent precleaning", Generation.TENURED, Concurrency.CONCURRENT, GcPattern.GC);

// IBM Types
// TODO: are scavenge always young only??
public static final Type IBM_AF = new Type("af", Generation.YOUNG);
Expand Down
Loading