Skip to content

Commit

Permalink
JITWatch initial support for ARM CPUs
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswhocodes committed Mar 5, 2017
1 parent 1120f1d commit 4d1d360
Show file tree
Hide file tree
Showing 22 changed files with 1,073 additions and 546 deletions.
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2016 Chris Newland. * Copyright (c) 2013-2017 Chris Newland.
* Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
* Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
*/ */
Expand All @@ -24,8 +24,10 @@
import org.adoptopenjdk.jitwatch.model.MemberSignatureParts; import org.adoptopenjdk.jitwatch.model.MemberSignatureParts;
import org.adoptopenjdk.jitwatch.model.MetaClass; import org.adoptopenjdk.jitwatch.model.MetaClass;
import org.adoptopenjdk.jitwatch.model.PackageManager; import org.adoptopenjdk.jitwatch.model.PackageManager;
import org.adoptopenjdk.jitwatch.model.assembly.Architecture;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyMethod; import org.adoptopenjdk.jitwatch.model.assembly.AssemblyMethod;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyUtil; import org.adoptopenjdk.jitwatch.model.assembly.AssemblyUtil;
import org.adoptopenjdk.jitwatch.model.assembly.IAssemblyParser;
import org.adoptopenjdk.jitwatch.util.StringUtil; import org.adoptopenjdk.jitwatch.util.StringUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
Expand All @@ -46,6 +48,8 @@ public class AssemblyProcessor


private List<AssemblyMethod> assemblyMethods = new ArrayList<>(); private List<AssemblyMethod> assemblyMethods = new ArrayList<>();


private Architecture architecture = null;

public AssemblyProcessor() public AssemblyProcessor()
{ {
} }
Expand Down Expand Up @@ -77,6 +81,23 @@ public void handleLine(final String inLine)
logger.debug("handleLine:{}", line); 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 (S_HASH.equals(previousLine) && line.startsWith("{method}"))
{ {
if (DEBUG_LOGGING_ASSEMBLY) if (DEBUG_LOGGING_ASSEMBLY)
Expand Down Expand Up @@ -149,20 +170,25 @@ else if (methodInterrupted && line.endsWith(S_APOSTROPHE))


public void complete() public void complete()
{ {
if (DEBUG_LOGGING_ASSEMBLY)
{
// logger.debug("completed assembly\n{}", builder.toString());
}

String asmString = builder.toString().trim(); String asmString = builder.toString().trim();


if (asmString.length() > 0) if (asmString.length() > 0)
{ {
AssemblyMethod assemblyMethod = AssemblyUtil.parseAssembly(asmString); IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(architecture);


assemblyMethod.setNativeAddress(nativeAddress); if (parser != null)
{
if (DEBUG_LOGGING_ASSEMBLY)
{
logger.debug("Using Assembly Parser {}", parser.getClass().getName());
}


assemblyMethods.add(assemblyMethod); AssemblyMethod assemblyMethod = parser.parseAssembly(asmString);

assemblyMethod.setNativeAddress(nativeAddress);

assemblyMethods.add(assemblyMethod);
}
} }


builder.delete(0, builder.length()); builder.delete(0, builder.length());
Expand All @@ -176,7 +202,7 @@ public void attachAssemblyToMembers(PackageManager packageManager)
for (AssemblyMethod assemblyMethod : assemblyMethods) for (AssemblyMethod assemblyMethod : assemblyMethods)
{ {
String asmSignature = assemblyMethod.getAssemblyMethodSignature(); String asmSignature = assemblyMethod.getAssemblyMethodSignature();

MemberSignatureParts msp = null; MemberSignatureParts msp = null;


IMetaMember currentMember = null; IMetaMember currentMember = null;
Expand All @@ -187,11 +213,11 @@ public void attachAssemblyToMembers(PackageManager packageManager)


if (DEBUG_LOGGING_ASSEMBLY) if (DEBUG_LOGGING_ASSEMBLY)
{ {
logger.debug("Parsed assembly sig {}\nfrom {}", msp, asmSignature); logger.debug("Parsed assembly sig\n{}\nfrom {}", msp, asmSignature);
} }


MetaClass metaClass = packageManager.getMetaClass(msp.getFullyQualifiedClassName()); MetaClass metaClass = packageManager.getMetaClass(msp.getFullyQualifiedClassName());

if (metaClass != null) if (metaClass != null)
{ {
currentMember = metaClass.getMemberForSignature(msp); currentMember = metaClass.getMemberForSignature(msp);
Expand All @@ -215,7 +241,7 @@ public void attachAssemblyToMembers(PackageManager packageManager)
{ {
logger.debug("Found member {}", currentMember); logger.debug("Found member {}", currentMember);
} }

currentMember.addAssembly(assemblyMethod); currentMember.addAssembly(assemblyMethod);


if (DEBUG_LOGGING_ASSEMBLY) if (DEBUG_LOGGING_ASSEMBLY)
Expand All @@ -227,7 +253,7 @@ public void attachAssemblyToMembers(PackageManager packageManager)
{ {
if (DEBUG_LOGGING_ASSEMBLY) if (DEBUG_LOGGING_ASSEMBLY)
{ {
logger.debug("Didn't find member for {}", msp); logger.debug("Didn't find member for\n{}", msp);
} }
} }
} }
Expand Down
@@ -0,0 +1,185 @@
package org.adoptopenjdk.jitwatch.model.assembly;

import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_ASSEMBLY;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.NATIVE_CODE_ENTRY_POINT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ENTITY_APOS;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HASH;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HEX_POSTFIX;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HEX_PREFIX;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_SQUARE_BRACKET;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_QUOTE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SEMICOLON;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AbstractAssemblyParser implements IAssemblyParser
{
protected static final Logger logger = LoggerFactory.getLogger(AbstractAssemblyParser.class);

protected Architecture architecture;

public AbstractAssemblyParser(Architecture architecture)
{
this.architecture = architecture;
}

public Architecture getArchitecture()
{
return architecture;
}

// TODO this is too much work
// save the string blocks and parse on demand
public AssemblyMethod parseAssembly(final String assemblyString)
{
final AssemblyLabels labels = new AssemblyLabels();

String[] lines = assemblyString.split(S_NEWLINE);

StringBuilder headerBuilder = new StringBuilder();

AssemblyBlock currentBlock = new AssemblyBlock();
currentBlock.setTitle(NATIVE_CODE_ENTRY_POINT);

AssemblyInstruction lastInstruction = null;

String lastLine = null;

AssemblyMethod method = null;

for (int i = 0; i < lines.length; i++)
{
if (DEBUG_LOGGING_ASSEMBLY)
{
logger.debug("line: '{}'", lines[i]);
}

if (i == 0)
{
method = new AssemblyMethod(lines[i], architecture);
}

String line = lines[i].replace(S_ENTITY_APOS, S_QUOTE);
line = line.replaceFirst("^ +", "");

if (line.startsWith(S_HASH))
{
if (DEBUG_LOGGING_ASSEMBLY)
{
logger.debug("Assembly header: {}", line);
}

headerBuilder.append(line).append(S_NEWLINE);
}
else if (line.startsWith(S_OPEN_SQUARE_BRACKET))
{
if (DEBUG_LOGGING_ASSEMBLY)
{
logger.debug("new AssemblyBlock: {}", line);
}

method.addBlock(currentBlock);
currentBlock = new AssemblyBlock();
currentBlock.setTitle(line);
}
else if (line.startsWith(S_SEMICOLON))
{
if (DEBUG_LOGGING_ASSEMBLY)
{
logger.debug("Extended comment? '{}'", line);
}

if (lastInstruction != null)
{
lastInstruction.addCommentLine(line);
}
}
else
{
AssemblyInstruction instr = createInstruction(labels, line);

if (instr == null && lastLine.trim().startsWith(S_HASH) && !line.startsWith(S_HEX_PREFIX)
&& !line.contains(' ' + S_HEX_PREFIX))
{
// remove last newline
headerBuilder.setLength(headerBuilder.length() - S_NEWLINE.length());

headerBuilder.append(line).append(S_NEWLINE);

// update untrimmedLine since it is used to update
// lastUntrimmedLine at end of loop
line = lastLine + line;
}
else if (instr == null && lastLine.trim().startsWith(S_SEMICOLON) && lastInstruction != null)
{
lastInstruction.appendToLastCommentLine(line);

// update untrimmedLine since it is used to update
// lastUntrimmedLine at end of loop
line = lastLine + line;
}
else
{
boolean replaceLast = false;
if (instr == null && i < lines.length - 1)
{
// try appending current and next lines together
String nextUntrimmedLine = lines[i + 1].replace(S_ENTITY_APOS, S_QUOTE);

instr = createInstruction(labels, line + nextUntrimmedLine);

if (instr != null)
{
i++;
}
}

if (instr == null && lastInstruction != null)
{
// try appending last and current lines together
instr = createInstruction(labels, lastLine + line);
if (instr != null)
{
replaceLast = true;
}
}

if (instr != null)
{
if (replaceLast)
{
currentBlock.replaceLastInstruction(instr);
}
else
{
currentBlock.addInstruction(instr);
}

lastInstruction = instr;
}
else
{
logger.error("Could not parse assembly: {}", line);
}
}
}
lastLine = line;
}

method.addBlock(currentBlock);

method.setHeader(headerBuilder.toString());

labels.buildLabels();

return method;
}

@Override
public boolean isAddress(String mnemonic, String operand)
{
return (operand.startsWith(S_HEX_PREFIX) || operand.endsWith(S_HEX_POSTFIX)) && isJump(mnemonic);
}
}
@@ -1,11 +1,42 @@
/*
* Copyright (c) 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; package org.adoptopenjdk.jitwatch.model.assembly;


import org.adoptopenjdk.jitwatch.util.StringUtil;

import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_APOSTROPHE;

public enum Architecture public enum Architecture
{ {
X86_32, X86_64, ARM_32, ARM_64; X86_32, X86_64, ARM_32, ARM_64;


private static final String ARCH_X86_32 = "i386";
private static final String ARCH_X86_64 = "i386:x86-64";
private static final String ARCH_ARM_32 = "arm";
private static final String ARCH_ARM_64 = "arm-64";

public static Architecture parseFromLogLine(String line) public static Architecture parseFromLogLine(String line)
{ {
return X86_64; String arch = StringUtil.getSubstringBetween(line, S_APOSTROPHE, S_APOSTROPHE);

if (arch != null)
{
switch (arch)
{
case ARCH_X86_32:
return X86_32;
case ARCH_X86_64:
return X86_64;
case ARCH_ARM_32:
return ARM_32;
case ARCH_ARM_64:
return ARM_64;
}
}

return null;
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2016 Chris Newland. * Copyright (c) 2013-2017 Chris Newland.
* Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
* Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
*/ */
Expand All @@ -22,9 +22,17 @@ public class AssemblyMethod


private String assemblyMethodSignature; private String assemblyMethodSignature;


public AssemblyMethod(String assemblyMethodSignature) private Architecture architecture;

public AssemblyMethod(String assemblyMethodSignature, Architecture architecture)
{ {
this.assemblyMethodSignature = assemblyMethodSignature; this.assemblyMethodSignature = assemblyMethodSignature;
this.architecture = architecture;
}

public Architecture getArchitecture()
{
return architecture;
} }


public String getAssemblyMethodSignature() public String getAssemblyMethodSignature()
Expand Down

0 comments on commit 4d1d360

Please sign in to comment.