/*
* 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.test;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.*;
import static org.junit.Assert.*;
import java.util.List;
import org.adoptopenjdk.jitwatch.model.assembly.Architecture;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyBlock;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyInstruction;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyLabels;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyMethod;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyUtil;
import org.adoptopenjdk.jitwatch.model.assembly.IAssemblyParser;
import org.junit.Test;
public class TestAssemblyParserX86 extends AbstractAssemblyTest
{
@Test
public void testAssemblyParse()
{
testAssemblyParse(new String[] {
"# {method} 'add' '(JJ)J' in 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'",
"# this: rsi:rsi = 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'",
"# parm0: rdx:rdx = long",
"# parm1: rcx:rcx = long",
"# [sp+0x20] (sp of caller)",
"0x00007f4475904140: mov 0x8(%rsi),%r10d",
"0x00007f4475904144: cmp %r10,%rax",
"0x00007f4475904147: jne 0x00007f44758c5960 ; {runtime_call}",
"0x00007f447590414d: data32 xchg %ax,%ax",
"[Verified Entry Point]",
"0x00007f4475904150: sub $0x18,%rsp",
"0x00007f4475904157: mov %rbp,0x10(%rsp) ;*synchronization entry",
" ; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@-1 (line 144)",
"0x00007f447590415c: mov %rdx,%rax",
"0x00007f447590415f: add %rcx,%rax ;*ladd",
" ; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@2 (line 144)",
"0x00007f4475904162: add $0x10,%rsp",
"0x00007f4475904166: pop %rbp",
"0x00007f4475904167: test %eax,0x5ce1e93(%rip) # 0x00007f447b5e6000",
" ; {poll_return}",
"0x00007f447590416d: retq",
"0x00007f447590416e: hlt",
"0x00007f447590416f: hlt",
"0x00007f4475904170: hlt",
"0x00007f4475904171: hlt",
"0x00007f4475904172: hlt",
"0x00007f4475904173: hlt",
"0x00007f4475904174: hlt",
"0x00007f4475904175: hlt",
"0x00007f4475904176: hlt",
"0x00007f4475904177: hlt",
"0x00007f4475904178: hlt",
"0x00007f4475904179: hlt",
"0x00007f447590417a: hlt",
"0x00007f447590417b: hlt",
"0x00007f447590417c: hlt",
"0x00007f447590417d: hlt",
"0x00007f447590417e: hlt",
"0x00007f447590417f: hlt",
"[Exception Handler]",
"[Stub Code]",
"0x00007f4475904180: jmpq 0x00007f44758ed2a0 ; {no_reloc}",
"[Deopt Handler Code]",
"0x00007f4475904185: callq 0x00007f447590418a",
"0x00007f447590418a: subq $0x5,(%rsp)",
"0x00007f447590418f: jmpq 0x00007f44758c6b00 ; {runtime_call}",
"0x00007f4475904194: hlt",
"0x00007f4475904195: hlt",
"0x00007f4475904196: hlt",
"0x00007f4475904197: hlt", });
}
@Test
public void testAssemblyParseBrokenHeader()
{
testAssemblyParse(new String[] {
"# {method} 'add' '(JJ)J' in 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'",
"# this: rsi:rsi = 'org/adoptopenjdk/jitwatch",
"/demo/MakeHotSpotLog'",
"# parm0: rdx:rdx = long",
"# parm1: rcx:rcx = long",
"# [sp+0x20] (sp of caller)",
"0x00007f4475904140: mov 0x8(%rsi),%r10d",
"0x00007f4475904144: cmp %r10,%rax",
"0x00007f4475904147: jne 0x00007f44758c5960 ; {runtime_call}",
"0x00007f447590414d: data32 xchg %ax,%ax",
"[Verified Entry Point]",
"0x00007f4475904150: sub $0x18,%rsp",
"0x00007f4475904157: mov %rbp,0x10(%rsp) ;*synchronization entry",
" ; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@-1 (line 144)",
"0x00007f447590415c: mov %rdx,%rax",
"0x00007f447590415f: add %rcx,%rax ;*ladd",
" ; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@2 (line 144)",
"0x00007f4475904162: add $0x10,%rsp",
"0x00007f4475904166: pop %rbp",
"0x00007f4475904167: test %eax,0x5ce1e93(%rip) # 0x00007f447b5e6000",
" ; {poll_return}",
"0x00007f447590416d: retq",
"0x00007f447590416e: hlt",
"0x00007f447590416f: hlt",
"0x00007f4475904170: hlt",
"0x00007f4475904171: hlt",
"0x00007f4475904172: hlt",
"0x00007f4475904173: hlt",
"0x00007f4475904174: hlt",
"0x00007f4475904175: hlt",
"0x00007f4475904176: hlt",
"0x00007f4475904177: hlt",
"0x00007f4475904178: hlt",
"0x00007f4475904179: hlt",
"0x00007f447590417a: hlt",
"0x00007f447590417b: hlt",
"0x00007f447590417c: hlt",
"0x00007f447590417d: hlt",
"0x00007f447590417e: hlt",
"0x00007f447590417f: hlt",
"[Exception Handler]",
"[Stub Code]",
"0x00007f4475904180: jmpq 0x00007f44758ed2a0 ; {no_reloc}",
"[Deopt Handler Code]",
"0x00007f4475904185: callq 0x00007f447590418a",
"0x00007f447590418a: subq $0x5,(%rsp)",
"0x00007f447590418f: jmpq 0x00007f44758c6b00 ; {runtime_call}",
"0x00007f4475904194: hlt",
"0x00007f4475904195: hlt",
"0x00007f4475904196: hlt",
"0x00007f4475904197: hlt", });
}
@Test
public void testAssemblyParseBrokenInsn()
{
testAssemblyParse(new String[] {
"# {method} 'add' '(JJ)J' in 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'",
"# this: rsi:rsi = 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'",
"# parm0: rdx:rdx = long",
"# parm1: rcx:rcx = long",
"# [sp+0x20] (sp of caller)",
"0x00007f4475904140: mov 0x8(%rsi),%r10d",
"0x00007f4475904144: cmp %r10,%rax",
"0x00007f4475904147: jne 0x00007f44758c5960 ; {runtime_call}",
"0x00007f447590414d: data32 xchg %ax,%ax",
"[Verified Entry Point]",
"0x00007f4475904150: sub $0x18,%rsp",
"0x00007f4475904157: mov %rbp,0x10(%rsp) ;*synchronization entry",
" ; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@-1 (line 144)",
"0x00007f447590415c: mov %rdx,",
"%rax",
"0x00007f447590415f: add %rcx,%rax ;*ladd",
" ; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@2 (line 144)",
"0x00007f4475904162: add $0x10,%rsp",
"0x00007f4475904166: pop %rbp",
"0x00007f4475904167: test %eax,0x5ce1e93(%rip) # 0x00007f447b5e6000",
" ; {poll_return}",
"0x00007f447590416d: retq",
"0x00007f447590416e: hlt",
"0x00007f447590416f: hlt",
"0x00007f4475904170: hlt",
"0x00007f4475904171: hlt",
"0x00007f4475904172: hlt",
"0x00007f4475904173: hlt",
"0x00007f4475904174: hlt",
"0x00007f4475904175: hlt",
"0x00007f4475904176: hlt",
"0x00007f4475904177: hlt",
"0x00007f4475904178: hlt",
"0x00007f4475904179: hlt",
"0x00007f447590417a: hlt",
"0x00007f447590417b: hlt",
"0x00007f447590417c: hlt",
"0x00007f447590417d: hlt",
"0x00007f447590417e: hlt",
"0x00007f447590417f: hlt",
"[Exception Handler]",
"[Stub Code]",
"0x00007f4475904180: jmpq 0x00007f44758ed2a0 ; {no_reloc}",
"[Deopt Handler Code]",
"0x00007f4475904185: callq 0x00007f447590418a",
"0x00007f447590418a: subq $0x5,(%rsp)",
"0x00007f447590418f: jmpq 0x00007f44758c6b00 ; {runtime_call}",
"0x00007f4475904194: hlt",
"0x00007f4475904195: hlt",
"0x00007f4475904196: hlt",
"0x00007f4475904197: hlt", });
}
@Test
public void testAssemblyParseBrokenComment()
{
testAssemblyParse(new String[] {
"# {method} 'add' '(JJ)J' in 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'",
"# this: rsi:rsi = 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'",
"# parm0: rdx:rdx = long",
"# parm1: rcx:rcx = long",
"# [sp+0x20] (sp of caller)",
"0x00007f4475904140: mov 0x8(%rsi),%r10d",
"0x00007f4475904144: cmp %r10,%rax",
"0x00007f4475904147: jne 0x00007f44758c5960 ; {runtime_call}",
"0x00007f447590414d: data32 xchg %ax,%ax",
"[Verified Entry Point]",
"0x00007f4475904150: sub $0x18,%rsp",
"0x00007f4475904157: mov %rbp,0x10(%rsp) ;*synchronization entry",
" ; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@-1 (line 144)",
"0x00007f447590415c: mov %rdx,%rax",
"0x00007f447590415f: add %rcx,%rax ;*ladd",
" ; ",
"- org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@2 (line 144)",
"0x00007f4475904162: add $0x10,%rsp",
"0x00007f4475904166: pop %rbp",
"0x00007f4475904167: test %eax,0x5ce1e93(%rip) # 0x00007f447b5e6000",
" ; {poll_return}",
"0x00007f447590416d: retq",
"0x00007f447590416e: hlt",
"0x00007f447590416f: hlt",
"0x00007f4475904170: hlt",
"0x00007f4475904171: hlt",
"0x00007f4475904172: hlt",
"0x00007f4475904173: hlt",
"0x00007f4475904174: hlt",
"0x00007f4475904175: hlt",
"0x00007f4475904176: hlt",
"0x00007f4475904177: hlt",
"0x00007f4475904178: hlt",
"0x00007f4475904179: hlt",
"0x00007f447590417a: hlt",
"0x00007f447590417b: hlt",
"0x00007f447590417c: hlt",
"0x00007f447590417d: hlt",
"0x00007f447590417e: hlt",
"0x00007f447590417f: hlt",
"[Exception Handler]",
"[Stub Code]",
"0x00007f4475904180: jmpq 0x00007f44758ed2a0 ; {no_reloc}",
"[Deopt Handler Code]",
"0x00007f4475904185: callq 0x00007f447590418a",
"0x00007f447590418a: subq $0x5,(%rsp)",
"0x00007f447590418f: jmpq 0x00007f44758c6b00 ; {runtime_call}",
"0x00007f4475904194: hlt",
"0x00007f4475904195: hlt",
"0x00007f4475904196: hlt",
"0x00007f4475904197: hlt", });
}
public void testAssemblyParse(String[] asm)
{
StringBuilder builder = new StringBuilder();
for (String line : asm)
{
builder.append(line).append(S_NEWLINE);
}
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyMethod asmMethod = parser.parseAssembly(builder.toString());
String header = asmMethod.getHeader();
assertNotNull(header);
assertEquals("# this: rsi:rsi = 'org/adoptopenjdk/jitwatch/demo/MakeHotSpotLog'", header.split("\n")[1]);
List<AssemblyBlock> blocks = asmMethod.getBlocks();
assertEquals(5, blocks.size());
AssemblyBlock block0 = blocks.get(0);
assertEquals("[Entry Point]", block0.getTitle());
List<AssemblyInstruction> instructions0 = block0.getInstructions();
assertEquals(4, instructions0.size());
AssemblyBlock block1 = blocks.get(1);
assertEquals("[Verified Entry Point]", block1.getTitle());
List<AssemblyInstruction> instructions1 = block1.getInstructions();
assertEquals(26, instructions1.size());
assertEquals(2, instructions1.get(2).getOperands().size());
assertEquals(";*ladd\n; - org.adoptopenjdk.jitwatch.demo.MakeHotSpotLog::add@2 (line 144)",
instructions1.get(3).getComment());
AssemblyBlock block2 = blocks.get(2);
assertEquals("[Exception Handler]", block2.getTitle());
List<AssemblyInstruction> instructions2 = block2.getInstructions();
assertEquals(0, instructions2.size());
AssemblyBlock block3 = blocks.get(3);
assertEquals("[Stub Code]", block3.getTitle());
List<AssemblyInstruction> instructions3 = block3.getInstructions();
assertEquals(1, instructions3.size());
AssemblyBlock block4 = blocks.get(4);
assertEquals("[Deopt Handler Code]", block4.getTitle());
List<AssemblyInstruction> instructions4 = block4.getInstructions();
assertEquals(7, instructions4.size());
}
@Test
public void testInstructionParse()
{
String line = "0x00007f4475904140: mov 0x8(%rsi),%r10d ;comment";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("7f4475904140", 16), instr.getAddress());
assertEquals("mov", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(2, operands.size());
assertEquals("0x8(%rsi)", operands.get(0));
assertEquals("%r10d", operands.get(1));
assertEquals(";comment", instr.getComment());
}
@Test
public void testInstructionParseWithModifier()
{
String line = "0x00007f447590414d: data32 xchg %ax,%ax";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("7f447590414d", 16), instr.getAddress());
assertEquals(1, instr.getPrefixes().size());
assertEquals("data32", instr.getPrefixes().get(0));
assertEquals("xchg", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(2, operands.size());
assertEquals("%ax", operands.get(0));
assertEquals("%ax", operands.get(1));
assertEquals(S_EMPTY, instr.getComment());
}
@Test
public void testInstructionParseNoOperands()
{
String line = "0x00007f447590416e: hlt";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("7f447590416e", 16), instr.getAddress());
assertEquals(0, instr.getPrefixes().size());
assertEquals("hlt", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(0, operands.size());
assertEquals(S_EMPTY, instr.getComment());
}
@Test
public void testInstructionParseMultiplePrefixes()
{
String line = "0x00007fbbc41082e5: data32 data32 nopw 0x0(%rax,%rax,1)";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("7fbbc41082e5", 16), instr.getAddress());
assertEquals(2, instr.getPrefixes().size());
assertEquals("data32", instr.getPrefixes().get(0));
assertEquals("data32", instr.getPrefixes().get(1));
assertEquals("nopw", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(1, operands.size());
assertEquals("0x0(%rax,%rax,1)", operands.get(0));
}
@Test
public void testInstructionParseRegression1()
{
String line = "0x00007f54f9bfd2f0: mov %eax,-0x14000(%rsp)";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("7f54f9bfd2f0", 16), instr.getAddress());
assertEquals(0, instr.getPrefixes().size());
assertEquals("mov", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(2, operands.size());
assertEquals("%eax", operands.get(0));
assertEquals("-0x14000(%rsp)", operands.get(1));
}
@Test
public void testInstructionParseIntelFormat()
{
String line = "0x0000000110aa2ee5: mov QWORD PTR [rsp+0x78],rax";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("110aa2ee5", 16), instr.getAddress());
assertEquals(0, instr.getPrefixes().size());
assertEquals("mov", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(2, operands.size());
assertEquals("QWORD PTR [rsp+0x78]", operands.get(0));
assertEquals("rax", operands.get(1));
}
@Test
public void testInstructionParseIntelFormatWithPrefixes()
{
String line = "0x0000000110aa2ee5: data32 data32 nop WORD PTR [rax+rax*1+0x0]";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("110aa2ee5", 16), instr.getAddress());
assertEquals(2, instr.getPrefixes().size());
assertEquals("data32", instr.getPrefixes().get(0));
assertEquals("data32", instr.getPrefixes().get(1));
assertEquals("nop", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(1, operands.size());
assertEquals("WORD PTR [rax+rax*1+0x0]", operands.get(0));
}
@Test
public void testInstructionParseIntelFormatWithPrefixes2()
{
String line = "0x00000001024a210c: lock idiv DWORD PTR [rax]";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("1024a210c", 16), instr.getAddress());
assertEquals(1, instr.getPrefixes().size());
assertEquals("lock", instr.getPrefixes().get(0));
assertEquals("idiv", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(1, operands.size());
assertEquals("DWORD PTR [rax]", operands.get(0));
}
@Test
public void testInstructionParseIntelFormatWithPrefixes3()
{
String line = "0x00000001024a18bc: data16 data16 xchg ax,ax";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("1024a18bc", 16), instr.getAddress());
assertEquals(2, instr.getPrefixes().size());
assertEquals("data16", instr.getPrefixes().get(0));
assertEquals("data16", instr.getPrefixes().get(1));
assertEquals("xchg", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(2, operands.size());
assertEquals("ax", operands.get(0));
assertEquals("ax", operands.get(1));
}
@Test
public void testInstructionParseIntelFormatStubCall()
{
String line = "0x0000000106d035e0: call Stub::jshort_disjoint_arraycopy";
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
AssemblyInstruction instr = parser.createInstruction(new AssemblyLabels(), line);
assertNotNull(instr);
assertEquals(Long.parseLong("106d035e0", 16), instr.getAddress());
assertEquals(0, instr.getPrefixes().size());
assertEquals("call", instr.getMnemonic());
List<String> operands = instr.getOperands();
assertEquals(1, operands.size());
assertEquals("Stub::jshort_disjoint_arraycopy", operands.get(0));
}
private void testOperand(OperandType type, String mnemonic, String operand)
{
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
switch (type)
{
case ADDRESS:
assertTrue(parser.isAddress(mnemonic, operand));
assertFalse(parser.isConstant(mnemonic, operand));
assertFalse(parser.isRegister(mnemonic, operand));
break;
case CONSTANT:
assertFalse(parser.isAddress(mnemonic, operand));
assertTrue(parser.isConstant(mnemonic, operand));
assertFalse(parser.isRegister(mnemonic, operand));
break;
case REGISTER:
assertFalse(parser.isAddress(mnemonic, operand));
assertFalse(parser.isConstant(mnemonic, operand));
assertTrue(parser.isRegister(mnemonic, operand));
break;
}
}
@Test
public void testIdentifyOperands()
{
testOperand(OperandType.ADDRESS, "callq", "0x00000001118fb8e0");
testOperand(OperandType.ADDRESS, "jmpq", "0x00000001118473c0");
testOperand(OperandType.ADDRESS, "call", "2f9f160h");
testOperand(OperandType.CONSTANT, "movabs", "0x00000001118473c0");
testOperand(OperandType.CONSTANT, "movabs", "0x11d38f7f0");
testOperand(OperandType.CONSTANT, "movabs", "$0x0");
testOperand(OperandType.CONSTANT, "movabs", "0x0");
testOperand(OperandType.CONSTANT, "mov", "118h");
testOperand(OperandType.REGISTER, "mov", "0x48(%rsp)");
testOperand(OperandType.REGISTER, "mov", "QWORD PTR [rsp+0x60]");
testOperand(OperandType.REGISTER, "pop", "rbp");
testOperand(OperandType.REGISTER, "pop", "%rbp");
testOperand(OperandType.REGISTER, "mov", "-0x14000(%rsp)");
testOperand(OperandType.REGISTER, "lea", "[rdi+1h]");
testOperand(OperandType.REGISTER, "mov", "dword ptr [rsp+0ffffffffffffa000h]");
}
@Test
public void testExtractRegisterName()
{
IAssemblyParser parser = AssemblyUtil.getParserForArchitecture(Architecture.X86_64);
assertEquals("rsp", parser.extractRegisterName("rsp"));
assertEquals("rbp", parser.extractRegisterName("%rbp"));
assertEquals("r10", parser.extractRegisterName("*%r10"));
assertEquals("rsp", parser.extractRegisterName("QWORD PTR [rsp+0x60]"));
assertEquals("rsp", parser.extractRegisterName("-0x14000(%rsp)"));
assertEquals("rax", parser.extractRegisterName("[rax+rax*1+0x0]"));
assertEquals("rdx", parser.extractRegisterName("(%rdx,%rbx,1)"));
}
}