/* * Copyright (c) 2013-2017 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ package org.adoptopenjdk.jitwatch.model.assembly; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_ASSEMBLY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.NATIVE_CODE_METHOD_MARK; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.NATIVE_CODE_START; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_APOSTROPHE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HASH; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SPACE; import java.util.ArrayList; import java.util.List; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_COLON; import org.adoptopenjdk.jitwatch.model.IMetaMember; import org.adoptopenjdk.jitwatch.model.LogParseException; import org.adoptopenjdk.jitwatch.model.MemberSignatureParts; import org.adoptopenjdk.jitwatch.model.MetaClass; import org.adoptopenjdk.jitwatch.model.PackageManager; import org.adoptopenjdk.jitwatch.util.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AssemblyProcessor { private static final Logger logger = LoggerFactory.getLogger(AssemblyProcessor.class); private StringBuilder builder = new StringBuilder(); private boolean assemblyStarted = false; private boolean methodStarted = false; private boolean methodInterrupted = false; private String previousLine = null; private String nativeAddress = null; private List<AssemblyMethod> assemblyMethods = new ArrayList<>(); private Architecture architecture = null; public AssemblyProcessor() { } public List<AssemblyMethod> getAssemblyMethods() { return assemblyMethods; } public void clear() { assemblyMethods.clear(); builder.delete(0, builder.length()); nativeAddress = null; previousLine = null; assemblyStarted = false; methodStarted = false; methodInterrupted = false; } public void handleLine(final String inLine) { String line = inLine.replaceFirst("^ +", ""); line = StringUtil.replaceXMLEntities(line); if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("handleLine:{}", line); } if (line.startsWith("[Disassembling for mach")) { architecture = Architecture.parseFromLogLine(line); if (architecture == null) { logger.error("Could not determine architecture from '{}'", line); } else { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Detected architecture: {}", architecture); } } } if (S_HASH.equals(previousLine) && line.startsWith("{method}")) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("fixup mangled {method} line"); } line = S_HASH + S_SPACE + line; } if (line.startsWith(NATIVE_CODE_START)) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Assembly started"); } assemblyStarted = true; if (builder.length() > 0) { complete(); } nativeAddress = StringUtil.getSubstringBetween(line, NATIVE_CODE_START, S_COLON).trim(); } else if (assemblyStarted) { boolean couldBeNativeMethodMark = false; couldBeNativeMethodMark = line.startsWith(NATIVE_CODE_METHOD_MARK); if (couldBeNativeMethodMark) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Assembly method started"); } methodStarted = true; if (!line.endsWith(S_APOSTROPHE)) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Method signature interrupted"); } methodInterrupted = true; } } else if (methodInterrupted && line.endsWith(S_APOSTROPHE)) { methodInterrupted = false; } if (methodStarted && line.length() > 0) { builder.append(line); if (!methodInterrupted) { builder.append(S_NEWLINE); } } } previousLine = line; } public void complete() { String asmString = builder.toString().trim(); if (asmString.length() > 0) { IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(architecture); if (parser != null) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Using Assembly Parser {}", parser.getClass().getName()); } AssemblyMethod assemblyMethod = parser.parseAssembly(asmString); assemblyMethod.setNativeAddress(nativeAddress); assemblyMethods.add(assemblyMethod); } } builder.delete(0, builder.length()); methodStarted = false; methodInterrupted = false; } public void attachAssemblyToMembers(PackageManager packageManager) { for (AssemblyMethod assemblyMethod : assemblyMethods) { String asmSignature = assemblyMethod.getAssemblyMethodSignature(); MemberSignatureParts msp = null; IMetaMember currentMember = null; try { msp = MemberSignatureParts.fromAssembly(asmSignature); if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Parsed assembly sig\n{}\nfrom {}", msp, asmSignature); } MetaClass metaClass = packageManager.getMetaClass(msp.getFullyQualifiedClassName()); if (metaClass != null) { currentMember = metaClass.getMemberForSignature(msp); } else { if (DEBUG_LOGGING) { logger.debug("No MetaClass found for {}", msp.getFullyQualifiedClassName()); } } } catch (LogParseException e) { logger.error("Could not parse MSP from line: {}", asmSignature, e); } if (currentMember != null) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Found member {}", currentMember); } currentMember.addAssembly(assemblyMethod); if (DEBUG_LOGGING_ASSEMBLY) { logger.info("Set assembly on {} {}", currentMember, assemblyMethod.toString()); } } else { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Didn't find member for\n{}", msp); } } } } }