Skip to content

Commit 23d0551

Browse files
committed
Add support for Go GC logging
The Go programming language has a very dense GC logging format, which is difficult to read for humans. It looks like this: gc 1 @0.027s 35%: 15+3.3+0.41 ms clock, 46+0.66/2.3/3.0+1.2 ms cpu... gc 2 @0.060s 28%: 3.6+7.6+0.37 ms clock, 10+0.33/0/6.5+1.1 ms cpu,... gc 3 @0.079s 26%: 0.072+6.7+0.68 ms clock, 0.21+3.9/0.060/11+2.0 m... gc 4 @0.104s 21%: 0.16+14+0.31 ms clock, 0.48+5.2/0.16/14+0.94 ms ... gc 5 @0.140s 18%: 0.12+14+0.73 ms clock, 0.36+3.8/4.1/16+2.1 ms cp... gc 6 @0.179s 17%: 0.20+15+0.48 ms clock, 0.62+5.1/10/19+1.4 ms cpu... gc 7 @0.222s 16%: 0.12+19+0.21 ms clock, 0.37+11/0.064/19+0.64 ms ... gc 8 @0.292s 15%: 0.10+13+0.34 ms clock, 0.32+10/11/13+1.0 ms cpu,... gc 9 @0.936s 4%: 0.053+25+0.67 ms clock, 0.16+0.30/0.36/30+2.0 ms ... gc 10 @1.123s 5%: 0.073+30+0.52 ms clock, 0.22+1.7/29/1.4+1.5 ms c... gc 11 @1.369s 4%: 0.066+34+0.47 ms clock, 0.19+10/20/2.1+1.4 ms cp... It fits well into the GCViewer model, although Go has nothing to do with the JVM.
1 parent 7ec56ac commit 23d0551

File tree

6 files changed

+772
-0
lines changed

6 files changed

+772
-0
lines changed

src/main/java/com/tagtraum/perf/gcviewer/imp/DataReaderFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ else if (s.indexOf("starting collection, threshold allocation reached.") != -1)
207207
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: IBM i5/OS 1.4.2");
208208
return new DataReaderIBMi5OS1_4_2(gcResource, in);
209209
}
210+
else if (s.contains("\ngc ")) {
211+
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: Go");
212+
return new DataReaderGo(gcResource, in);
213+
}
210214
return null;
211215
}
212216

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.tagtraum.perf.gcviewer.imp;
2+
3+
import com.tagtraum.perf.gcviewer.model.AbstractGCEvent;
4+
import com.tagtraum.perf.gcviewer.model.GCEvent;
5+
import com.tagtraum.perf.gcviewer.model.GCModel;
6+
import com.tagtraum.perf.gcviewer.model.GCResource;
7+
import com.tagtraum.perf.gcviewer.util.ParseInformation;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.io.LineNumberReader;
11+
import java.io.UnsupportedEncodingException;
12+
import java.util.logging.Level;
13+
import java.util.regex.Matcher;
14+
import java.util.regex.Pattern;
15+
16+
/**
17+
* Parses GC log output from Go 1.9.
18+
*
19+
* @author <a href="mailto:roland.illig@gmx.de">Roland Illig</a>
20+
* @see <a href="https://golang.org/pkg/runtime/#hdr-Environment_Variables">Go documentation</a>
21+
*/
22+
public class DataReaderGo extends AbstractDataReader {
23+
24+
private static final Pattern GCLINE = Pattern.compile(""
25+
+ "gc "
26+
+ "(\\d+) "
27+
+ "@(\\d+\\.\\d+)s "
28+
+ "(\\d+)%: "
29+
+ "(\\d+(?:\\.\\d+)?)\\+"
30+
+ "(\\d+(?:\\.\\d+)?)\\+"
31+
+ "(\\d+(?:\\.\\d+)?) ms clock, "
32+
+ "(\\d+(?:\\.\\d+)?)\\+"
33+
+ "(\\d+(?:\\.\\d+)?)/"
34+
+ "(\\d+(?:\\.\\d+)?)/"
35+
+ "(\\d+(?:\\.\\d+)?)\\+"
36+
+ "(\\d+(?:\\.\\d+)?) ms cpu, "
37+
+ "(\\d+)->"
38+
+ "(\\d+)->"
39+
+ "(\\d+) MB, "
40+
+ "(\\d+) MB goal, "
41+
+ "(\\d+) P");
42+
43+
public DataReaderGo(GCResource gcResource, InputStream in) throws UnsupportedEncodingException {
44+
super(gcResource, in);
45+
}
46+
47+
public GCModel read() throws IOException {
48+
if (getLogger().isLoggable(Level.INFO)) getLogger().info("Reading Go format...");
49+
50+
try (LineNumberReader in = this.in) {
51+
GCModel model = new GCModel();
52+
model.setFormat(GCModel.Format.GO);
53+
ParseInformation parsePosition = new ParseInformation(0);
54+
55+
Matcher matcher = GCLINE.matcher("");
56+
String line;
57+
while ((line = in.readLine()) != null && shouldContinue()) {
58+
parsePosition.setIndex(0);
59+
parsePosition.setLineNumber(in.getLineNumber());
60+
if (!matcher.reset(line).matches()) {
61+
continue;
62+
}
63+
64+
try {
65+
AbstractGCEvent<?> gcEvent = parseMatch(matcher);
66+
model.add(gcEvent);
67+
} catch (Exception pe) {
68+
if (getLogger().isLoggable(Level.WARNING)) getLogger().warning(pe.toString());
69+
if (getLogger().isLoggable(Level.FINE)) getLogger().log(Level.FINE, pe.getMessage(), pe);
70+
}
71+
}
72+
return model;
73+
} finally {
74+
if (getLogger().isLoggable(Level.INFO)) getLogger().info("Done reading.");
75+
}
76+
}
77+
78+
private AbstractGCEvent<?> parseMatch(Matcher matcher) {
79+
double relativeTime = Double.parseDouble(matcher.group(2));
80+
double stopTheWorld1Time = Double.parseDouble(matcher.group(4)) / 1000.0;
81+
double stopTheWorld2Time = Double.parseDouble(matcher.group(6)) / 1000.0;
82+
int preUsed = Integer.parseInt(matcher.group(12)) * 1024;
83+
int alive = Integer.parseInt(matcher.group(13)) * 1024;
84+
int postUsed = Integer.parseInt(matcher.group(14)) * 1024;
85+
86+
double pause = stopTheWorld1Time + stopTheWorld2Time;
87+
return new GCEvent(relativeTime, preUsed, postUsed, alive, pause, AbstractGCEvent.Type.GC);
88+
}
89+
}

src/main/java/com/tagtraum/perf/gcviewer/model/GCModel.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,5 +1065,6 @@ public String toString() {
10651065
public static final Format IBM_VERBOSE_GC = new Format("IBM -verbose:gc");
10661066
public static final Format SUN_1_2_2VERBOSE_GC = new Format("Sun 1.2.2 -verbose:gc");
10671067
public static final Format UNIFIED_JVM_LOGGING = new Format("Unified jvm logging -Xlog:gc");
1068+
public static final Format GO = new Format("Go");
10681069
}
10691070
}

src/test/java/com/tagtraum/perf/gcviewer/UnittestHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class UnittestHelper {
3131

3232
private static final String FOLDER_OPENJDK = "openjdk";
3333
public enum FOLDER {
34+
GO("go"),
3435
HP("hp"),
3536
IBM("ibm"),
3637
JROCKIT("jrockit"),
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.tagtraum.perf.gcviewer.imp;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import com.tagtraum.perf.gcviewer.UnittestHelper;
6+
import com.tagtraum.perf.gcviewer.model.GCModel;
7+
import com.tagtraum.perf.gcviewer.model.GcResourceFile;
8+
import java.io.ByteArrayInputStream;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import org.junit.Test;
12+
13+
public class TestDataReaderGo {
14+
15+
@Test
16+
public void test() throws IOException {
17+
String gcLog = ""
18+
+ "gc starting...\n" // Such a line is not produced by the Go GC; it is just for testing
19+
+ "gc 1 @997.597s 3%: 68+0.36+0.51 ms clock, 205+0/16/89+1.5 ms cpu, 84->84->42 MB, 86 MB goal, 3 P\n"
20+
+ "a line unrelated to GC logging\n"
21+
+ "gc 2 @997.597s 3%: 68+0.36+0.51 ms clock, 205+0/16/89+1.5 ms cpu, 11111111111111111111111111111111111->84->42 MB, 86 MB goal, 3 P\n"
22+
+ "gc 3 @997.597s 3%: 68+0.36+0.51 ms clock, 205+0/16/89+1.5 ms cpu, 84->84->42 MB, 86 MB goal, 3 P\n";
23+
ByteArrayInputStream in = new ByteArrayInputStream(gcLog.getBytes("US-ASCII"));
24+
DataReader reader = new DataReaderGo(new GcResourceFile("byteArray"), in);
25+
GCModel model = reader.read();
26+
27+
assertEquals(2, model.size());
28+
}
29+
30+
@Test
31+
public void exampleLog() throws IOException {
32+
String fileName = "go1.9.txt";
33+
InputStream in = UnittestHelper.getResourceAsStream(UnittestHelper.FOLDER.GO, fileName);
34+
DataReader reader = new DataReaderGo(new GcResourceFile(fileName), in);
35+
GCModel model = reader.read();
36+
37+
assertEquals(635, model.size());
38+
}
39+
}

0 commit comments

Comments
 (0)