Metadata-Version: 2.4
Name: komaru
Version: 0.1.0.post1
Summary: TUI mapping CPython bytecode to the interpreter's native opcode handlers (tail-call builds)
Author-email: Kirill Podoprigora <kirill.bast@gmail.com>
License-Expression: MIT
Keywords: assembly,bytecode,cpython,disassembly,interpreter,tui
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Interpreters
Requires-Python: >=3.11
Requires-Dist: capstone>=5.0
Requires-Dist: pyelftools>=0.31
Requires-Dist: rich>=13.0
Requires-Dist: textual>=0.80
Description-Content-Type: text/markdown

# komaru

Three synchronised views of the same Python code: the source, the `dis`
bytecode, and **the machine code the CPython interpreter actually executes
for each opcode** — extracted from a real interpreter binary, never
synthesised.

komaru is not a compiler and not a JIT. It is an inspection tool built on
one observation: CPython 3.14+ compiled with `--with-tail-call-interp`
emits **one function per opcode**, each with its own symbol
(`_TAIL_CALL_LOAD_FAST`, `_TAIL_CALL_BINARY_OP_ADD_INT`, …). That turns
"which instructions does this opcode run?" into a mechanical symbol-table
lookup — no debug-info heuristics, no guessing.

```text
│ offset │ opcode                │ argrepr │ native   │  0x23aed0  mov  qword ptr [r12+0x38], r15
│      0 │ RESUME_CHECK          │         │ 71B/20i  │  0x23aed8  mov  rax, [r12+rax*8+0x50]
│      4 │ LOAD_FAST_BORROW_L... │ a, b    │ 74B/22i  │  0x23aedd  test al, 1
│      6 │ BINARY_OP_ADD_INT     │ +       │ 375B/100i│  ...
│     18 │ RETURN_VALUE          │         │ 156B/47i │  0x23af03  jmp  rcx   [dispatch]
```

Selecting a bytecode instruction highlights the source span it came from
and shows its handler's disassembly, with resolved call targets
(`PyNumber_Add`, `_Py_Dealloc`, …) and the dispatch tail tagged so it can
be hidden.

## Requirements

- A CPython **≥ 3.14** built with `--with-tail-call-interp` (clang ≥ 19 or
  GCC ≥ 15), unstripped — the handler symbols are local to `.symtab`:

  ```sh
  cd cpython && mkdir build && cd build
  CC=clang ../configure --with-tail-call-interp OPT="-Og -g" && make -j
  ```

- Linux/ELF, x86-64 (aarch64 code paths exist but are unverified).
- Any Python ≥ 3.11 to run the TUI itself. The target interpreter and the
  TUI's interpreter are fully decoupled; only the target must match the
  binary the index was extracted from.

## Usage

```sh
komaru                # first run: point it at your tail-call python;
                      # the handler index is extracted and cached
komaru script.py      # inspect a script (bare path implies `run`)
komaru module.pyc     # inspect compiled bytecode
```

Everything is reachable from inside the TUI:

| key | action |
|-----|--------|
| `f` | open a `.py` / `.pyc` file |
| `w` | set warmup runs and re-probe |
| `a` | toggle adaptive (specialised) bytecode |
| `t` | show/hide the dispatch tail |
| `o` | pick a nested code object (comprehensions, lambdas, methods) |
| `b` | switch to a different CPython build |
| `/` | search opnames / argreprs |
| `e` | export the current view to markdown |
| `Tab` / `↑` `↓` | move between panes / instructions |

Extracted indexes are cached in `~/.cache/komaru/`, keyed by binary; a
rebuilt CPython is detected and re-extracted automatically. Explicit
control, for scripting:

```sh
komaru extract build/python -o index.json
komaru extract build/libpython3.14.so.1.0 --python build/python -o index.json
komaru run script.py --target-func myfunc --warmup 3 --index index.json
komaru run --code 'a + b'
komaru run --target-func json:dumps
```

Index discovery order: `--index` → `$KOMARU_INDEX` → `./index.json` → cache.

## Adaptive mode

The specialising interpreter rewrites `BINARY_OP` into `BINARY_OP_ADD_INT`
(and friends) after enough executions — and each specialisation has its own
handler with completely different machine code. Warmup (`w`, or
`--warmup N`) executes the script/module body N times so this actually
happens; your script is responsible for calling the functions you care
about. Execution counts are observed with `sys.monitoring`, so the UI says
explicitly when a code object never ran instead of showing a meaningless
adaptive view.

## Correctness

- Every native instruction shown comes from capstone over bytes read out
  of the binary. Missing data is reported as unavailable — never filled in.
- Opcode names and numbering come from interrogating the target
  interpreter, never from the host's `opcode` module. Nothing is hardcoded.
- Each index records the exact target version; a mismatched index/
  interpreter pair is refused, since opcode numbering changes every release.
- An opcode with no handler symbol in the index is reported as such in the
  UI rather than silently blanked.

## Scope

Out of scope for now, in rough order of likelihood: macOS/Mach-O backend,
Tier 2 uop expansion, JIT stencil inspection, `perf` integration, Windows
(blocked on PE symbol availability for static handler functions).

## Development

```sh
uv sync
uv run pytest                      # unit tests run against a checked-in ELF fixture
KOMARU_CPYTHON_BUILDS=/path/to/cpython/build uv run pytest   # + integration tests
uv run ruff check . && uv run ruff format --check .
uv run mypy src tests
```
