-
Notifications
You must be signed in to change notification settings - Fork 981
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
Add support for Go GC logging #196
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package com.tagtraum.perf.gcviewer.imp; | ||
|
||
import com.tagtraum.perf.gcviewer.model.AbstractGCEvent; | ||
import com.tagtraum.perf.gcviewer.model.GCEvent; | ||
import com.tagtraum.perf.gcviewer.model.GCModel; | ||
import com.tagtraum.perf.gcviewer.model.GCResource; | ||
import com.tagtraum.perf.gcviewer.util.ParseInformation; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.LineNumberReader; | ||
import java.io.UnsupportedEncodingException; | ||
import java.util.logging.Level; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Parses GC log output from Go 1.9. | ||
* | ||
* @author <a href="mailto:[email protected]">Roland Illig</a> | ||
* @see <a href="https://golang.org/pkg/runtime/#hdr-Environment_Variables">Go documentation</a> | ||
*/ | ||
public class DataReaderGo extends AbstractDataReader { | ||
|
||
private static final Pattern GCLINE = Pattern.compile("" | ||
+ "gc " | ||
+ "(\\d+) " | ||
+ "@(\\d+\\.\\d+)s " | ||
+ "(\\d+)%: " | ||
+ "(\\d+(?:\\.\\d+)?)\\+" | ||
+ "(\\d+(?:\\.\\d+)?)\\+" | ||
+ "(\\d+(?:\\.\\d+)?) ms clock, " | ||
+ "(\\d+(?:\\.\\d+)?)\\+" | ||
+ "(\\d+(?:\\.\\d+)?)/" | ||
+ "(\\d+(?:\\.\\d+)?)/" | ||
+ "(\\d+(?:\\.\\d+)?)\\+" | ||
+ "(\\d+(?:\\.\\d+)?) ms cpu, " | ||
+ "(\\d+)->" | ||
+ "(\\d+)->" | ||
+ "(\\d+) MB, " | ||
+ "(\\d+) MB goal, " | ||
+ "(\\d+) P"); | ||
|
||
public DataReaderGo(GCResource gcResource, InputStream in) throws UnsupportedEncodingException { | ||
super(gcResource, in); | ||
} | ||
|
||
public GCModel read() throws IOException { | ||
if (getLogger().isLoggable(Level.INFO)) getLogger().info("Reading Go format..."); | ||
|
||
try (LineNumberReader in = this.in) { | ||
GCModel model = new GCModel(); | ||
model.setFormat(GCModel.Format.GO); | ||
ParseInformation parsePosition = new ParseInformation(0); | ||
|
||
Matcher matcher = GCLINE.matcher(""); | ||
String line; | ||
while ((line = in.readLine()) != null && shouldContinue()) { | ||
parsePosition.setIndex(0); | ||
parsePosition.setLineNumber(in.getLineNumber()); | ||
if (!matcher.reset(line).matches()) { | ||
continue; | ||
} | ||
|
||
try { | ||
AbstractGCEvent<?> gcEvent = parseMatch(matcher); | ||
model.add(gcEvent); | ||
} catch (Exception pe) { | ||
if (getLogger().isLoggable(Level.WARNING)) getLogger().warning(pe.toString()); | ||
if (getLogger().isLoggable(Level.FINE)) getLogger().log(Level.FINE, pe.getMessage(), pe); | ||
} | ||
} | ||
return model; | ||
} finally { | ||
if (getLogger().isLoggable(Level.INFO)) getLogger().info("Done reading."); | ||
} | ||
} | ||
|
||
private AbstractGCEvent<?> parseMatch(Matcher matcher) { | ||
double relativeTime = Double.parseDouble(matcher.group(2)); | ||
double stopTheWorld1Time = Double.parseDouble(matcher.group(4)) / 1000.0; | ||
double stopTheWorld2Time = Double.parseDouble(matcher.group(6)) / 1000.0; | ||
int preUsed = Integer.parseInt(matcher.group(12)) * 1024; | ||
int alive = Integer.parseInt(matcher.group(13)) * 1024; | ||
int postUsed = Integer.parseInt(matcher.group(14)) * 1024; | ||
|
||
double pause = stopTheWorld1Time + stopTheWorld2Time; | ||
return new GCEvent(relativeTime, preUsed, postUsed, alive, pause, AbstractGCEvent.Type.GC); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.tagtraum.perf.gcviewer.imp; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
import com.tagtraum.perf.gcviewer.UnittestHelper; | ||
import com.tagtraum.perf.gcviewer.model.GCModel; | ||
import com.tagtraum.perf.gcviewer.model.GcResourceFile; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import org.junit.Test; | ||
|
||
public class TestDataReaderGo { | ||
|
||
@Test | ||
public void test() throws IOException { | ||
String gcLog = "" | ||
+ "gc starting...\n" // Such a line is not produced by the Go GC; it is just for testing | ||
+ "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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, the pause for this event is 68 + 0.51 ms. What about the 0.36 ms? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The 0.36 ms is part of the GC time. I did not count it since I was only interested in the stop-the-world time. You can add it if you want. As I said above, the GC fields were not documented enough for me to decide which duration to include and which to omit. |
||
+ "a line unrelated to GC logging\n" | ||
+ "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" | ||
+ "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"; | ||
ByteArrayInputStream in = new ByteArrayInputStream(gcLog.getBytes("US-ASCII")); | ||
DataReader reader = new DataReaderGo(new GcResourceFile("byteArray"), in); | ||
GCModel model = reader.read(); | ||
|
||
assertEquals(2, model.size()); | ||
} | ||
|
||
@Test | ||
public void exampleLog() throws IOException { | ||
String fileName = "go1.9.txt"; | ||
InputStream in = UnittestHelper.getResourceAsStream(UnittestHelper.FOLDER.GO, fileName); | ||
DataReader reader = new DataReaderGo(new GcResourceFile(fileName), in); | ||
GCModel model = reader.read(); | ||
|
||
assertEquals(635, model.size()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you only using groups 4 + 6 for the pause time? What is group 5?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Group 5 is the "concurrent mark and scan". I left it out because I only considered the stop-the-world times relevant. Maybe I'm wrong, though. I was missing some Javadoc on the fields of the
GCEvent
class, therefore I could only guess what is important and what is not.The full documentation is at https://golang.org/pkg/runtime/#hdr-Environment_Variables, the paragraph starting with
gctrace:
. I thought mentioning the above URL in the Javadoc would be sufficient. But sure, it wouldn't hurt to document each capturing group.