package com.ibm.nmon.parser.gc.state; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.TimeZone; import com.ibm.nmon.data.DataRecord; import com.ibm.nmon.parser.gc.GCParserContext; /** * Base class for Java 6 style verbose GC cycles. */ abstract class Java6GCCycle extends JavaGCCycle { private final SimpleDateFormat datetime = new SimpleDateFormat("MMM dd HH:mm:ss yyyy", java.util.Locale.US); // the XML element name for the GC cycle protected final String transitionElement; // the DataType field for the GC cycle private final String intervalField; // rather than parse the timestamp for each cycle, which is accurate only to the second, // parse the interval ms, which is microseconds accurate, and keep an accumulated total // from the first timestamp seen in the file for each gc type private long baseTime; private double accumulatedTime; // state for handling <gc> elements private final Java6Collection collection; Java6GCCycle(String transitionElement, String intervalField) { super(); this.transitionElement = transitionElement; this.intervalField = intervalField; this.collection = new Java6Collection(this); } @Override public final GCState startElement(GCParserContext context, String elementName, String unparsedAttributes) { if (transitionElement.equals(elementName)) { // Java6GC will have already called context.parseAttributes() return onStartCycle(context, elementName, unparsedAttributes); } else if ("gc".equals(elementName)) { beforeGC = false; return collection.startElement(context, elementName, unparsedAttributes); } else if ("time".equals(elementName)) { context.parseAttributes(unparsedAttributes); if (beforeGC) { double exclusive = context.parseDouble("exclusiveaccessms"); if (exclusive > (86400 * 1000)) { context.logInvalidValue("exlusiveaccessms", context.getAttribute("exclusiveaccessms")); error = true; } else { context.setValue("GCTIME", "exclusive_ms", exclusive); } } else { context.setValue("GCTIME", "total_ms", "totalms"); } } else if ("nursery".equals(elementName)) { context.parseAttributes(unparsedAttributes); calculateSizes(context, elementName, "freebytes", "totalbytes"); } else if ("tenured".equals(elementName)) { context.parseAttributes(unparsedAttributes); calculateSizes(context, elementName, "freebytes", "totalbytes"); } else if ("refs_cleared".equals(elementName) || "refs".equals(elementName)) { context.parseAttributes(unparsedAttributes); context.setValue("GCSTAT", "soft", "soft"); context.setValue("GCSTAT", "weak", "weak"); context.setValue("GCSTAT", "phantom", "phantom"); } else if ("minimum".equals(elementName)) { context.parseAttributes(unparsedAttributes); context.setValue("GCMEM", "requested", "requested_bytes"); } return this; } @Override public GCState endElement(GCParserContext context, String elementName) { if (elementName.equals(transitionElement)) { if (!error) { calculateTotalSizes(context); context.saveRecord(); } error = false; beforeGC = true; return Java6GC.INSTANCE; } else { return this; } } @Override public void reset() { super.reset(); this.baseTime = 0; this.accumulatedTime = 0; } protected GCState onStartCycle(GCParserContext context, String elementName, String unparsedAttributes) { calculateTime(context); return this; } protected final void calculateTime(GCParserContext context) { boolean first = false; if (baseTime == 0) { baseTime = parseTimestamp(context); if (baseTime == 0) { error = true; return; } first = true; } // use the interval milliseconds to calculate the actual timestamp for the DataRecord double intervalms = context.parseDouble("intervalms"); if (Double.isNaN(intervalms)) { error = true; return; } // sanity check for bugs seen in AIX verbose GC logs with incorrectly huge intervals // assume it is nearly impossible to not run GC once a day if (intervalms > (86400 * 1000)) { context.logInvalidValue("intervalms", context.getAttribute("intervalms")); error = true; return; } accumulatedTime += intervalms; context.setCurrentRecord(new DataRecord((long) (baseTime + accumulatedTime), String.format("%08x", context.getData().getRecordCount()).toString())); if (!first) { // do not record a zero for the first interval context.setValue("GCSINCE", intervalField, intervalms / 1000); } } void setTimeZone(TimeZone timeZone) { datetime.setTimeZone(timeZone); } private long parseTimestamp(GCParserContext context) { String value = context.getAttribute("timestamp"); if (value == null) { context.logMissingAttribute("timestamp"); return 0; } long toReturn; try { toReturn = datetime.parse(value).getTime(); } catch (ParseException pe) { context.logInvalidValue("timestamp", value); toReturn = 0; } return toReturn; } }