Skip to content

Commit

Permalink
Add branch count
Browse files Browse the repository at this point in the history
  • Loading branch information
romainguy committed May 16, 2024
1 parent d2bd45b commit 44b9b3a
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 20 deletions.
Binary file modified art/kotlin-explorer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ compose.desktop {
targetFormats(TargetFormat.Dmg)

packageName = "Kotlin Explorer"
packageVersion = "1.0.1"
packageVersion = "1.1.0"
description = "Kotlin Explorer"
vendor = "Romain Guy"
licenseFile = rootProject.file("LICENSE")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class ByteCodeParser {
val instructions = readInstructions()
val lineNumbers = readLineNumbers()

return Method(header, instructions.withLineNumbers(lineNumbers))
return Method(header, InstructionSet(instructions.withLineNumbers(lineNumbers), ISA.ByteCode))
}

private fun PeekingIterator<String>.readInstructions(): List<Instruction> {
Expand Down
6 changes: 3 additions & 3 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/code/Code.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ package dev.romainguy.kotlin.explorer.code
* A data model representing disassembled code
*
* Given a list [Class]'s constructs a mode that provides:
* * Disassembled text with optional line number annotations
* * Jump information for branch instructions
* - Disassembled text with optional line number annotations
* - Jump information for branch instructions
*/
class Code(
val text: String,
Expand All @@ -43,7 +43,7 @@ class Code(
startClass(clazz)
clazz.methods.forEach { method ->
startMethod(method)
method.instructions.forEach { instruction ->
method.instructionSet.instructions.forEach { instruction ->
writeInstruction(instruction)
}
endMethod()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,27 @@ class CodeBuilder(private val codeStyle: CodeStyle) {
sb.append(" ".repeat(codeStyle.indent))
writeLine(method.header)
sb.append(" ".repeat(codeStyle.indent))
writeLine("-- ${method.instructions.size} instructions")
writeLine("-- ${method.instructionSet.instructions.size} instructions")
val branches = countBranches(method.instructionSet)
if (branches > 0) {
sb.append(" ".repeat(codeStyle.indent))
writeLine("-- $branches branch${if (branches > 1) "es" else ""}")
}
}

private fun countBranches(instructionSet: InstructionSet): Int {
var count = 0
instructionSet.instructions.forEach { instruction ->
val code = instruction.code
val index = code.indexOf(": ")
instructionSet.isa.branchInstructions.forEach out@ { opCode ->
if (code.startsWith(opCode, index + 2)) {
count++
return@out
}
}
}
return count
}

fun endMethod() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ private fun Throwable.toFullString(): String {
printStackTrace(PrintStream(it))
it.toString()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,46 @@

package dev.romainguy.kotlin.explorer.code

enum class ISA(val branchInstructions: Array<String>) {
ByteCode(arrayOf("")),
Dex(arrayOf("if-")),
X86_64(
arrayOf(
"je",
"jz",
"jne",
"jnz",
"js",
"jns",
"jg",
"jnle",
"jge",
"jnl",
"jl",
"jnge",
"jle",
"jng",
"ja",
"jnbe",
"jae",
"jnb",
"jb",
"jnae",
"jbe",
"jna"
)
),
Arm64(arrayOf("b.", "b ", "bl", "cbz", "cbnz", "tbz", "tbnz"))
}

data class Class(val header: String, val methods: List<Method>)

data class Method(val header: String, val instructions: List<Instruction>)
data class Method(val header: String, val instructionSet: InstructionSet)

data class InstructionSet(val instructions: List<Instruction>, val isa: ISA)

data class Instruction(val address: Int, val code: String, val jumpAddress: Int?, val lineNumber: Int? = null)

fun List<Instruction>.withLineNumbers(lineNumbers: Map<Int, Int>): List<Instruction> {
return map { it.copy(lineNumber = lineNumbers[it.address]) }

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ internal class DexDumpParser {
val returnType = returnTypeFromType(type)
val paramTypes = paramTypesFromType(type).joinToString(", ")

return Method("$returnType $className.$name($paramTypes)", instructions.withLineNumbers(positions))
return Method(
"$returnType $className.$name($paramTypes)",
InstructionSet(instructions.withLineNumbers(positions), ISA.Dex)
)
}

private fun Iterator<String>.readInstructions(): List<Instruction> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,32 @@
package dev.romainguy.kotlin.explorer.oat

import dev.romainguy.kotlin.explorer.*
import dev.romainguy.kotlin.explorer.code.Class
import dev.romainguy.kotlin.explorer.code.CodeContent
import dev.romainguy.kotlin.explorer.code.*
import dev.romainguy.kotlin.explorer.code.CodeContent.Error
import dev.romainguy.kotlin.explorer.code.CodeContent.Success
import dev.romainguy.kotlin.explorer.code.Instruction
import dev.romainguy.kotlin.explorer.code.Method

private val ClassNameRegex = Regex("^\\d+: L(?<class>[^;]+); \\(offset=0x$HexDigit+\\) \\(type_idx=\\d+\\).+")
private val MethodRegex = Regex("^\\s+\\d+:\\s+(?<method>.+)\\s+\\(dex_method_idx=\\d+\\)")
private val CodeRegex = Regex("^\\s+0x(?<address>$HexDigit+):\\s+$HexDigit+\\s+(?<code>.+)")
private val X86JumpRegex = Regex(".+ [+-]\\d+ \\(0x(?<address>$HexDigit{8})\\)\$")
private val Arm64JumpRegex = Regex(".+ #[+-]0x$HexDigit+ \\(addr 0x(?<address>$HexDigit+)\\)\$")
private val X86JumpRegex = Regex(".+ [+-]\\d+ \\(0x(?<address>$HexDigit{8})\\)\$")

internal class OatDumpParser {
private var isa = ISA.Arm64

fun parse(text: String): CodeContent {
return try {
val lines = PeekingIterator(text.lineSequence().iterator())
val jumpRegex = when (val set = lines.readInstructionSet()) {
"X86_64" -> X86JumpRegex
"Arm64" -> Arm64JumpRegex
val isa = when (val set = lines.readInstructionSet()) {
"Arm64" -> ISA.Arm64
"X86_64" -> ISA.X86_64
else -> throw IllegalStateException("Unknown instruction set: $set")
}
val jumpRegex = when (isa) {
ISA.Arm64 -> Arm64JumpRegex
ISA.X86_64 -> X86JumpRegex
else -> throw IllegalStateException("Incompatible ISA: $isa")
}
val classes = buildList {
while (lines.hasNext()) {
val match = lines.consumeUntil(ClassNameRegex) ?: break
Expand Down Expand Up @@ -81,7 +85,7 @@ internal class OatDumpParser {
val method = match.getValue("method")
consumeUntil("CODE:")
val instructions = readInstructions(jumpRegex)
return Method(method, instructions)
return Method(method, InstructionSet(instructions, isa))
}

private fun PeekingIterator<String>.readInstructions(jumpRegex: Regex): List<Instruction> {
Expand Down

0 comments on commit 44b9b3a

Please sign in to comment.