/*
* Copyright (c) 2013-2016 Chris Newland.
* Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
* Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
*/
package org.adoptopenjdk.jitwatch.core;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_ANGLE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_ANGLE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SLASH;
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.DEBUG_LOGGING_TAGPROCESSOR;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_FRAGMENT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.TAG_OPEN_FRAGMENT;
import java.util.Map;
import java.util.Stack;
import org.adoptopenjdk.jitwatch.model.Tag;
import org.adoptopenjdk.jitwatch.model.Task;
import org.adoptopenjdk.jitwatch.model.bytecode.Opcode;
import org.adoptopenjdk.jitwatch.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TagProcessor
{
private static final Logger logger = LoggerFactory.getLogger(TagProcessor.class);
private Stack<String> methodIDStack = new Stack<>();
private Tag currentTag;
private Tag topTag = null;
private boolean fragmentSeen;
public String getTopTagName()
{
String result = null;
if (topTag != null)
{
result = topTag.getName();
}
return result;
}
public Tag processLine(String line)
{
Tag result = null;
if (line != null)
{
if (line.length() > 3 && line.charAt(0) == C_OPEN_ANGLE)
{
result = handleTag(line);
}
else if (currentTag != null)
{
String closingTag = currentTag.getClosingTag();
if (line.endsWith(closingTag))
{
line = line.substring(0, line.length() - closingTag.length());
currentTag.addTextContent(line);
processLine(closingTag);
}
else
{
currentTag.addTextContent(line);
}
}
else
{
if (DEBUG_LOGGING)
{
logger.debug("Did not handle: {}", line);
}
}
}
if (DEBUG_LOGGING_TAGPROCESSOR)
{
logger.debug("returning tag: {}", result);
}
if (result != null)
{
resetState();
}
return result;
}
public boolean wasFragmentSeen()
{
return fragmentSeen;
}
private void resetState()
{
currentTag = null;
topTag = null;
}
private Tag handleTag(String line)
{
Tag result = null;
if (DEBUG_LOGGING_TAGPROCESSOR)
{
logger.debug("Handling line: {}", line);
}
// closing tag
if (line.charAt(1) == C_SLASH)
{
String closeName = line.substring(2, line.length() - 1);
if (DEBUG_LOGGING_TAGPROCESSOR)
{
logger.debug("closeName:{}, currentTag:{}, topTag:{}", closeName,
currentTag == null ? "null" : currentTag.getName(), topTag == null ? "null" : topTag.getName());
}
if (currentTag != null && closeName.equals(currentTag.getName()))
{
if (currentTag.getParent() == null)
{
result = currentTag;
}
else
{
currentTag = currentTag.getParent();
}
if (JITWatchConstants.TAG_PARSE.equals(currentTag.getName()))
{
methodIDStack.pop();
}
}
else if (S_FRAGMENT.equals(closeName))
{
result = topTag;
}
}
else
{
boolean selfClosing = (line.charAt(line.length() - 2) == C_SLASH);
int indexEndName = line.indexOf(C_SPACE);
if (indexEndName == -1)
{
indexEndName = line.indexOf(C_CLOSE_ANGLE);
if (indexEndName > 0)
{
if (selfClosing)
{
indexEndName = line.length() - 2;
}
}
}
if (indexEndName != -1)
{
result = processValidLine(line, indexEndName, selfClosing);
}
}
return result;
}
private Tag processValidLine(String line, int indexEndName, boolean selfClosing)
{
if (DEBUG_LOGGING_TAGPROCESSOR)
{
logger.debug("processValidLine(line:{}, indexEndName:{}, selfClosing:{})", line, indexEndName, selfClosing);
}
Tag result = null;
String name = line.substring(1, indexEndName);
String attributeString = line.substring(indexEndName);
Map<String, String> attrs = StringUtil.attributeStringToMap(attributeString);
Tag nextTag;
if (JITWatchConstants.TAG_TASK.equals(name))
{
nextTag = new Task(attributeString, selfClosing);
}
else
{
nextTag = new Tag(name, attributeString, selfClosing);
}
if (DEBUG_LOGGING_TAGPROCESSOR)
{
logger.debug("top: {}", topTag);
}
if (DEBUG_LOGGING_TAGPROCESSOR)
{
logger.debug("currentTag: {}", currentTag);
}
if (DEBUG_LOGGING_TAGPROCESSOR)
{
logger.debug("t: {}", nextTag);
}
if (currentTag == null)
{
if (name.equals(S_FRAGMENT))
{
logger.warn(
"Found a {} in the HotSpot log. The VM exited before the hotspot log was fully written. JIT information may have been lost.",
TAG_OPEN_FRAGMENT);
fragmentSeen = true;
return null;
}
else
{
// new tag at top level
currentTag = nextTag;
topTag = nextTag;
}
}
else
{
currentTag.addChild(nextTag);
}
if (topTag instanceof Task)
{
switch (name)
{
case JITWatchConstants.TAG_TYPE:
((Task) topTag).addDictionaryType(attrs.get(JITWatchConstants.ATTR_ID), nextTag);
break;
case JITWatchConstants.TAG_METHOD:
((Task) topTag).addDictionaryMethod(attrs.get(JITWatchConstants.ATTR_ID), nextTag);
break;
case JITWatchConstants.TAG_KLASS:
((Task) topTag).addDictionaryKlass(attrs.get(JITWatchConstants.ATTR_ID), nextTag);
break;
case JITWatchConstants.TAG_PARSE:
String currentMethodID = attrs.get(JITWatchConstants.ATTR_METHOD);
methodIDStack.push(currentMethodID);
break;
case JITWatchConstants.TAG_BC:
String bci = attrs.get(JITWatchConstants.ATTR_BCI);
String code = attrs.get(JITWatchConstants.ATTR_CODE);
try
{
int bciValue = Integer.parseInt(bci);
int codeValue = Integer.parseInt(code);
Opcode opcode = Opcode.getByCode(codeValue);
((Task) topTag).addBCIOpcodeMapping(methodIDStack.peek(), bciValue, opcode);
//logger.info("{} got bc tag {}", methodIDStack.peek(), nextTag.toString(false));
}
catch (NumberFormatException nfe)
{
logger.error("Couldn't parse bc tag {}", nextTag);
}
break;
default:
break;
}
}
if (selfClosing)
{
if (name.equals(currentTag.getName()))
{
if (currentTag.getParent() == null)
{
result = currentTag;
}
}
}
else
{
// not closed
currentTag = nextTag;
}
return result;
}
}