package org.adoptopenjdk.jitwatch.parser;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_COMPILER;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_COMPILE_ID;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_COMPILE_KIND;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_FREE_CODE_CACHE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_METHOD;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_NMSIZE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_STAMP;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.ATTR_STAMP_COMPLETED;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C1;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C2;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C2N;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_QUOTE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.J9;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_DOT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SLASH;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.TAG_CODE_CACHE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.TAG_TASK;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.TAG_TASK_DONE;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.adoptopenjdk.jitwatch.core.IJITListener;
import org.adoptopenjdk.jitwatch.core.JITWatchConfig;
import org.adoptopenjdk.jitwatch.core.TagProcessor;
import org.adoptopenjdk.jitwatch.model.CodeCacheEvent;
import org.adoptopenjdk.jitwatch.model.CodeCacheEvent.CodeCacheEventType;
import org.adoptopenjdk.jitwatch.model.EventType;
import org.adoptopenjdk.jitwatch.model.IMetaMember;
import org.adoptopenjdk.jitwatch.model.JITDataModel;
import org.adoptopenjdk.jitwatch.model.JITEvent;
import org.adoptopenjdk.jitwatch.model.LogParseException;
import org.adoptopenjdk.jitwatch.model.MetaClass;
import org.adoptopenjdk.jitwatch.model.ParsedClasspath;
import org.adoptopenjdk.jitwatch.model.SplitLog;
import org.adoptopenjdk.jitwatch.model.Tag;
import org.adoptopenjdk.jitwatch.model.Task;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyProcessor;
import org.adoptopenjdk.jitwatch.util.ClassUtil;
import org.adoptopenjdk.jitwatch.util.ParseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractLogParser implements ILogParser
{
protected static final Logger logger = LoggerFactory.getLogger(AbstractLogParser.class);
protected JITDataModel model;
protected String vmCommand = null;
protected boolean reading = false;
protected boolean hasTraceClassLoad = false;
protected boolean hasParseError = false;
protected String errorDialogTitle;
protected String errorDialogBody;
protected IMetaMember currentMember = null;
protected IJITListener jitListener = null;
protected ILogParseErrorListener errorListener = null;
protected boolean inHeader = false;
protected long parseLineNumber;
protected long processLineNumber;
protected JITWatchConfig config = new JITWatchConfig();
protected TagProcessor tagProcessor;
protected AssemblyProcessor asmProcessor;
protected SplitLog splitLog = new SplitLog();
public AbstractLogParser(IJITListener jitListener)
{
model = new JITDataModel();
this.jitListener = jitListener;
}
@Override
public void setConfig(JITWatchConfig config)
{
this.config = config;
}
@Override
public JITWatchConfig getConfig()
{
return config;
}
@Override
public SplitLog getSplitLog()
{
return splitLog;
}
@Override
public ParsedClasspath getParsedClasspath()
{
return config.getParsedClasspath();
}
protected void configureDisposableClassLoader()
{
if (DEBUG_LOGGING)
{
logger.debug("configureDisposableClassLoader()");
}
List<String> configuredClassLocations = config.getConfiguredClassLocations();
List<String> parsedClassLocations = getParsedClasspath().getClassLocations();
int configuredClasspathCount = configuredClassLocations.size();
int parsedClasspathCount = parsedClassLocations.size();
List<URL> classpathURLList = new ArrayList<URL>(configuredClasspathCount + parsedClasspathCount);
for (String filename : configuredClassLocations)
{
URI uri = new File(filename).toURI();
jitListener.handleLogEntry("Adding configured classpath: " + uri.toString());
if (DEBUG_LOGGING)
{
logger.debug("adding to classpath {}", uri.toString());
}
try
{
classpathURLList.add(uri.toURL());
}
catch (MalformedURLException e)
{
logger.error("Could not create URL: {} ", uri, e);
}
}
for (String filename : getParsedClasspath().getClassLocations())
{
if (!configuredClassLocations.contains(filename))
{
URI uri = new File(filename).toURI();
jitListener.handleLogEntry("Adding parsed classpath: " + uri.toString());
try
{
classpathURLList.add(uri.toURL());
}
catch (MalformedURLException e)
{
logger.error("Could not create URL: {} ", uri, e);
}
}
}
ClassUtil.initialise(classpathURLList);
}
protected void logEvent(JITEvent event)
{
if (jitListener != null)
{
jitListener.handleJITEvent(event);
}
}
protected void logError(String entry)
{
if (jitListener != null)
{
jitListener.handleErrorEntry(entry);
}
}
@Override
public JITDataModel getModel()
{
return model;
}
@Override
public void reset()
{
if (DEBUG_LOGGING)
{
logger.debug("Log parser reset()");
}
ClassUtil.clear();
getModel().reset();
splitLog.clear();
hasTraceClassLoad = false;
hasParseError = false;
errorDialogTitle = null;
errorDialogBody = null;
reading = false;
inHeader = false;
currentMember = null;
vmCommand = null;
parseLineNumber = 0;
processLineNumber = 0;
tagProcessor = new TagProcessor();
asmProcessor = new AssemblyProcessor();
}
@Override
public void stopParsing()
{
reading = false;
}
public IMetaMember findMemberWithSignature(String logSignature)
{
IMetaMember result = null;
try
{
result = ParseUtil.findMemberWithSignature(model, logSignature);
}
catch (LogParseException ex)
{
if (DEBUG_LOGGING)
{
logger.debug("Could not parse signature: {}", logSignature);
logger.debug("Exception was {}", ex.getMessage());
}
logError("Could not parse line " + processLineNumber + " : " + logSignature + " : " + ex.getMessage());
}
return result;
}
@Override
public boolean hasParseError()
{
return hasParseError;
}
@Override
public String getVMCommand()
{
return vmCommand;
}
@Override
public void discardParsedLogs()
{
splitLog.clear();
splitLog = new SplitLog();
}
protected void addToClassModel(String fqClassName)
{
if (DEBUG_LOGGING)
{
logger.debug("addToClassModel {}", fqClassName);
}
Class<?> clazz = null;
MetaClass metaClass = model.getPackageManager().getMetaClass(fqClassName);
if (metaClass != null)
{
return;
}
try
{
clazz = ClassUtil.loadClassWithoutInitialising(fqClassName);
if (clazz != null)
{
model.buildAndGetMetaClass(clazz);
}
}
catch (ClassNotFoundException cnf)
{
if (!ParseUtil.possibleLambdaMethod(fqClassName))
{
logError("ClassNotFoundException: '" + fqClassName + C_QUOTE);
}
}
catch (NoClassDefFoundError ncdf)
{
logError("NoClassDefFoundError: '" + fqClassName + C_SPACE + "requires " + ncdf.getMessage() + C_QUOTE);
}
catch (UnsupportedClassVersionError ucve)
{
hasParseError = true;
errorDialogTitle = "UnsupportedClassVersionError for class " + fqClassName;
errorDialogBody = "Could not load " + fqClassName + " as the class file version is too recent for this JVM.";
logError(
"UnsupportedClassVersionError! Tried to load a class file with an unsupported format (later version than this JVM)");
logger.error("Class file for {} created in a later JVM version", fqClassName, ucve);
}
catch (Throwable t)
{
// Possibly a VerifyError
logger.error("Could not addClassToModel {}", fqClassName, t);
logError("Exception: '" + fqClassName + C_QUOTE);
}
}
private void logSplitStats()
{
logger.debug("Header lines : {}", splitLog.getHeaderLines().size());
logger.debug("ClassLoader lines : {}", splitLog.getClassLoaderLines().size());
logger.debug("LogCompilation lines: {}", splitLog.getCompilationLines().size());
logger.debug("Assembly lines : {}", splitLog.getAssemblyLines().size());
}
@Override
public void processLogFile(File logFile, ILogParseErrorListener errorListener)
{
reset();
configureDisposableClassLoader();
// tell listener to reset any data
jitListener.handleReadStart();
this.errorListener = errorListener;
splitLogFile(logFile);
if (DEBUG_LOGGING)
{
logSplitStats();
}
parseLogFile();
jitListener.handleReadComplete();
}
protected void handleTagQueued(Tag tag)
{
handleMethodLine(tag, EventType.QUEUE);
}
protected void handleTagNMethod(Tag tag)
{
Map<String, String> tagAttributes = tag.getAttributes();
String attrCompiler = tagAttributes.get(ATTR_COMPILER);
renameCompilationCompletedTimestamp(tag);
if (attrCompiler != null)
{
if (C1.equals(attrCompiler))
{
handleMethodLine(tag, EventType.NMETHOD_C1);
}
else if (C2.equals(attrCompiler))
{
handleMethodLine(tag, EventType.NMETHOD_C2);
}
else if (J9.equals(attrCompiler))
{
handleMethodLine(tag, EventType.NMETHOD_J9);
}
else
{
logError("Unexpected Compiler attribute: " + attrCompiler);
}
}
else
{
String attrCompileKind = tagAttributes.get(ATTR_COMPILE_KIND);
if (attrCompileKind != null && C2N.equals(attrCompileKind))
{
handleMethodLine(tag, EventType.NMETHOD_C2N);
}
else
{
logError("Missing Compiler attribute " + tag);
}
}
}
protected void handleTagTask(Task task)
{
handleMethodLine(task, EventType.TASK);
Tag tagCodeCache = task.getFirstNamedChild(TAG_CODE_CACHE);
Tag tagTaskDone = task.getFirstNamedChild(TAG_TASK_DONE);
if (tagTaskDone != null)
{
handleTaskDone(tagTaskDone);
if (tagCodeCache != null)
{
long stamp = ParseUtil.parseStampFromTag(tagTaskDone);
long freeCodeCache = ParseUtil.parseLongAttributeFromTag(tagCodeCache, ATTR_FREE_CODE_CACHE);
long nativeCodeSize = ParseUtil.parseLongAttributeFromTag(tagTaskDone, ATTR_NMSIZE);
storeCodeCacheEventDetail(CodeCacheEventType.COMPILATION, stamp, nativeCodeSize, freeCodeCache);
}
}
else
{
logger.error("{} not found in {}", TAG_TASK_DONE, task);
}
}
protected void storeCodeCacheEvent(CodeCacheEventType eventType, Tag tag)
{
storeCodeCacheEventDetail(eventType, ParseUtil.parseStampFromTag(tag), 0, 0);
}
private void storeCodeCacheEventDetail(CodeCacheEventType eventType, long stamp, long nativeCodeSize, long freeCodeCache)
{
CodeCacheEvent codeCacheEvent = new CodeCacheEvent(eventType, stamp, nativeCodeSize, freeCodeCache);
model.addCodeCacheEvent(codeCacheEvent);
}
private void handleMethodLine(Tag tag, EventType eventType)
{
Map<String, String> attrs = tag.getAttributes();
String attrMethod = attrs.get(ATTR_METHOD);
if (attrMethod != null)
{
attrMethod = attrMethod.replace(S_SLASH, S_DOT);
handleMember(attrMethod, attrs, eventType, tag);
}
}
private void handleMember(String signature, Map<String, String> attrs, EventType type, Tag tag)
{
IMetaMember metaMember = findMemberWithSignature(signature);
long stampTime = ParseUtil.getStamp(attrs);
if (metaMember != null)
{
switch (type)
{
case QUEUE:
{
metaMember.setTagTaskQueued(tag);
JITEvent queuedEvent = new JITEvent(stampTime, type, metaMember);
model.addEvent(queuedEvent);
logEvent(queuedEvent);
}
break;
case NMETHOD_C1:
case NMETHOD_C2:
case NMETHOD_C2N:
case NMETHOD_J9:
{
metaMember.setTagNMethod(tag);
metaMember.getMetaClass().incCompiledMethodCount();
model.updateStats(metaMember, attrs);
JITEvent compiledEvent = new JITEvent(stampTime, type, metaMember);
model.addEvent(compiledEvent);
logEvent(compiledEvent);
}
break;
case TASK:
{
metaMember.setTagTask((Task) tag);
currentMember = metaMember;
}
break;
default:
break;
}
}
}
private void handleTaskDone(Tag tagTaskDone)
{
Map<String, String> attrs = tagTaskDone.getAttributes();
if (attrs.containsKey(ATTR_NMSIZE))
{
long nmsize = Long.parseLong(attrs.get(ATTR_NMSIZE));
model.addNativeBytes(nmsize);
}
if (currentMember != null)
{
Tag parent = tagTaskDone.getParent();
String compileID = null;
if (TAG_TASK.equals(parent.getName()))
{
compileID = parent.getAttributes().get(ATTR_COMPILE_ID);
if (compileID != null)
{
currentMember.setTagTaskDone(compileID, tagTaskDone);
}
else
{
logger.warn("No compile_id attribute found on task");
}
}
else
{
logger.warn("Unexpected parent of task_done: {}", parent.getName());
}
// prevents attr overwrite by next task_done if next member not
// found due to classpath issues
currentMember = null;
}
}
private void renameCompilationCompletedTimestamp(Tag tag)
{
String compilationCompletedStamp = tag.getAttributes().get(ATTR_STAMP);
if (compilationCompletedStamp != null)
{
tag.getAttributes().remove(ATTR_STAMP);
tag.getAttributes().put(ATTR_STAMP_COMPLETED, compilationCompletedStamp);
}
}
protected abstract void parseLogFile();
protected abstract void splitLogFile(File logFile);
protected abstract void handleTag(Tag tag);
}