Skip to content

Commit 32ff444

Browse files
authored
better KMIR show & new kmir info to get the de-refed type (#648)
- provide more options to `kmir show`; - add a new command `kmir info` to show the dereferenced types.
1 parent ff9d9a6 commit 32ff444

11 files changed

+424
-13
lines changed

README.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,46 @@ uv --project kmir run kmir link file1.smir.json file2.smir.json file3.smir.json
9696
uv --project kmir run kmir link file1.smir.json file2.smir.json
9797
```
9898

99+
**`kmir info`** - Inspect SMIR JSON metadata (currently: types)
100+
```bash
101+
# Show information about specific type IDs in a SMIR JSON
102+
uv --project kmir run kmir info path/to/program.smir.json --types "1,2,3"
103+
104+
# Notes
105+
# - The --types option accepts a comma-separated list of numeric Stable MIR type IDs.
106+
# - Output format: one line per requested type, e.g.:
107+
# Type Ty(1): Int(....)
108+
# Type Ty(2): StructT(name=..., adt_def=..., fields=[...])
109+
# - If --types is omitted, the command currently produces no output.
110+
```
111+
99112
### Analysis Commands
100113

101-
**`kmir show`** - Display proof information
114+
**`kmir show`** - Display proof information with advanced filtering options
102115
```bash
116+
# Basic usage
103117
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir
118+
119+
# Show specific nodes only
120+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --nodes "1,2,3"
121+
122+
# Show node deltas (transitions between specific nodes)
123+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --node-deltas "1:2,3:4"
124+
125+
# Display full node information (default is compact)
126+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --full-printer
127+
128+
# Show static information cells (functions, types, etc.)
129+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --no-omit-static-info
130+
131+
# Show current body cell content
132+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --no-omit-current-body
133+
134+
# Omit specific cells from output
135+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --omit-cells "cell1,cell2"
136+
137+
# Combine multiple options for detailed analysis
138+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --full-printer --no-omit-static-info --nodes "1,2" --verbose
104139
```
105140

106141
**`kmir view`** - Detailed view of proof results
@@ -128,7 +163,16 @@ uv --project kmir run kmir show-rules proof_id source_node target_node --proof-d
128163

129164
3. **View Results**:
130165
```bash
166+
# Quick overview (compact format, static info hidden)
131167
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir
168+
169+
# Detailed analysis with full information
170+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --full-printer --no-omit-static-info
171+
172+
# Focus on specific nodes
173+
uv --project kmir run kmir show proof_id --proof-dir ./proof_dir --nodes "1,2,3"
174+
175+
# Interactive view
132176
uv --project kmir run kmir view proof_id --proof-dir ./proof_dir --verbose
133177
```
134178

@@ -137,6 +181,32 @@ uv --project kmir run kmir show-rules proof_id source_node target_node --proof-d
137181
uv --project kmir run kmir show-rules proof_id 1 3 --proof-dir ./proof_dir
138182
```
139183

184+
### Advanced Show Usage Examples
185+
186+
**Debugging workflow:**
187+
```bash
188+
# 1. Start with a quick overview
189+
uv --project kmir run kmir show my_proof --proof-dir ./proofs
190+
191+
# 2. Focus on problematic nodes (e.g., nodes 5, 6, 7)
192+
uv --project kmir run kmir show my_proof --proof-dir ./proofs --nodes "5,6,7"
193+
194+
# 3. Examine transitions between specific nodes
195+
uv --project kmir run kmir show my_proof --proof-dir ./proofs --node-deltas "5:6,6:7"
196+
197+
# 4. Get full details for deep debugging
198+
uv --project kmir run kmir show my_proof --proof-dir ./proofs --nodes "6" --full-printer --no-omit-static-info --no-omit-current-body
199+
```
200+
201+
**Performance analysis:**
202+
```bash
203+
# Hide verbose cells but show execution flow
204+
uv --project kmir run kmir show my_proof --proof-dir ./proofs --omit-cells "locals,heap" --verbose
205+
206+
# Focus on function calls and type information
207+
uv --project kmir run kmir show my_proof --proof-dir ./proofs --no-omit-static-info --omit-cells "currentBody"
208+
```
209+
140210
### Command Options
141211

142212
Most commands support:
@@ -146,6 +216,15 @@ Most commands support:
146216
- `--max-depth DEPTH`: Maximum execution depth
147217
- `--max-iterations ITERATIONS`: Maximum iterations
148218

219+
**`kmir show` specific options:**
220+
- `--nodes NODES`: Comma separated list of node IDs to show (e.g., "1,2,3")
221+
- `--node-deltas DELTAS`: Comma separated list of node deltas in format "source:target" (e.g., "1:2,3:4")
222+
- `--omit-cells CELLS`: Comma separated list of cell names to omit from output
223+
- `--full-printer`: Display the full node in output (default is compact)
224+
- `--no-omit-static-info`: Display static information cells (functions, start-symbol, types, adt-to-ty)
225+
- `--no-omit-current-body`: Display the `<currentBody>` cell completely
226+
- `--smir-info SMIR_INFO`: Path to SMIR JSON file to improve debug messaging
227+
149228
For complete options, use `--help` with each command.
150229

151230
### Supporters

kmir/src/kmir/__main__.py

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from .linker import link
2121
from .options import (
2222
GenSpecOpts,
23+
InfoOpts,
2324
LinkOpts,
2425
ProveRawOpts,
2526
ProveRSOpts,
@@ -30,7 +31,7 @@
3031
ViewOpts,
3132
)
3233
from .parse.parser import parse_json
33-
from .smir import SMIRInfo
34+
from .smir import SMIRInfo, Ty
3435

3536
if TYPE_CHECKING:
3637
from argparse import Namespace
@@ -130,14 +131,38 @@ def _kmir_view(opts: ViewOpts) -> None:
130131

131132

132133
def _kmir_show(opts: ShowOpts) -> None:
134+
from pyk.kast.pretty import PrettyPrinter
135+
136+
from .kprint import KMIRPrettyPrinter
137+
133138
kmir = KMIR(HASKELL_DEF_DIR, LLVM_LIB_DIR)
134139
proof = APRProof.read_proof_data(opts.proof_dir, opts.id)
135-
printer = PrettyPrinter(kmir.definition)
136-
omit_labels = ('<currentBody>',) if opts.omit_current_body else ()
137-
cterm_show = CTermShow(printer.print, omit_labels=omit_labels)
140+
141+
# Use custom KMIR printer by default, switch to standard printer if requested
142+
if opts.use_default_printer:
143+
printer = PrettyPrinter(kmir.definition)
144+
_LOGGER.info('Using standard PrettyPrinter')
145+
else:
146+
printer = KMIRPrettyPrinter(kmir.definition)
147+
_LOGGER.info('Using custom KMIRPrettyPrinter')
148+
149+
all_omit_cells = list(opts.omit_cells or ())
150+
if opts.omit_current_body:
151+
all_omit_cells.append('<currentBody>')
152+
153+
cterm_show = CTermShow(printer.print, omit_labels=tuple(all_omit_cells))
138154
node_printer = KMIRAPRNodePrinter(cterm_show, proof, opts)
139155
shower = APRProofShow(kmir.definition, node_printer=node_printer)
140-
lines = shower.show(proof)
156+
shower.kcfg_show.pretty_printer = printer
157+
_LOGGER.info(
158+
f'Showing {proof.id} with\n nodes: {opts.nodes},\n node_deltas: {opts.node_deltas},\n omit_cells: {tuple(all_omit_cells)}'
159+
)
160+
lines = shower.show(
161+
proof,
162+
nodes=opts.nodes or (),
163+
node_deltas=opts.node_deltas or (),
164+
omit_cells=tuple(all_omit_cells),
165+
)
141166
print('\n'.join(lines))
142167

143168

@@ -164,6 +189,16 @@ def _kmir_prune(opts: PruneOpts) -> None:
164189
proof.write_proof_data()
165190

166191

192+
def _kmir_info(opts: InfoOpts) -> None:
193+
smir_info = SMIRInfo.from_file(opts.smir_file)
194+
195+
if opts.types:
196+
print(f'\nTypes requested: {opts.types}')
197+
chosen_types = [Ty(type_id) for type_id in opts.types]
198+
for type_id in chosen_types:
199+
print(f'Type {type_id}: {smir_info.unref_type(type_id)}')
200+
201+
167202
def _kmir_link(opts: LinkOpts) -> None:
168203
result = link([SMIRInfo.from_file(f) for f in opts.smir_files])
169204
result.dump(opts.output_file)
@@ -178,6 +213,8 @@ def kmir(args: Sequence[str]) -> None:
178213
_kmir_run(opts)
179214
case GenSpecOpts():
180215
_kmir_gen_spec(opts)
216+
case InfoOpts():
217+
_kmir_info(opts)
181218
case ProveRawOpts():
182219
_kmir_prove_raw(opts)
183220
case ViewOpts():
@@ -221,6 +258,12 @@ def _arg_parser() -> ArgumentParser:
221258
'--start-symbol', type=str, metavar='SYMBOL', default='main', help='Symbol name to begin execution from'
222259
)
223260

261+
info_parser = command_parser.add_parser(
262+
'info', help='Show information about a SMIR JSON file', parents=[kcli_args.logging_args]
263+
)
264+
info_parser.add_argument('smir_file', metavar='FILE', help='SMIR JSON file to analyze')
265+
info_parser.add_argument('--types', metavar='TYPES', help='Comma separated list of type IDs to show details for')
266+
224267
prove_args = ArgumentParser(add_help=False)
225268
prove_args.add_argument('--proof-dir', metavar='DIR', help='Proof directory')
226269
prove_args.add_argument('--bug-report', metavar='PATH', help='path to optional bug report')
@@ -247,11 +290,11 @@ def _arg_parser() -> ArgumentParser:
247290

248291
display_args = ArgumentParser(add_help=False)
249292
display_args.add_argument(
250-
'--no-full-printer',
293+
'--full-printer',
251294
dest='full_printer',
252-
action='store_false',
253-
default=True,
254-
help='Do not display the full node in output.',
295+
action='store_true',
296+
default=False,
297+
help='Display the full node in output.',
255298
)
256299
display_args.add_argument(
257300
'--smir-info',
@@ -268,9 +311,30 @@ def _arg_parser() -> ArgumentParser:
268311
help='Display the <currentBody> cell completely.',
269312
)
270313

271-
command_parser.add_parser(
314+
show_parser = command_parser.add_parser(
272315
'show', help='Show proof information', parents=[kcli_args.logging_args, proof_args, display_args]
273316
)
317+
show_parser.add_argument('--nodes', metavar='NODES', help='Comma separated list of node IDs to show')
318+
show_parser.add_argument(
319+
'--node-deltas', metavar='DELTAS', help='Comma separated list of node deltas in format "source:target"'
320+
)
321+
show_parser.add_argument(
322+
'--omit-cells', metavar='CELLS', help='Comma separated list of cell names to omit from output'
323+
)
324+
show_parser.add_argument(
325+
'--no-omit-static-info',
326+
dest='omit_static_info',
327+
action='store_false',
328+
default=True,
329+
help='Display static information cells (functions, start-symbol, types, adt-to-ty)',
330+
)
331+
show_parser.add_argument(
332+
'--use-default-printer',
333+
dest='use_default_printer',
334+
action='store_true',
335+
default=False,
336+
help='Use standard PrettyPrinter instead of custom KMIR printer',
337+
)
274338

275339
show_rules_parser = command_parser.add_parser(
276340
'show-rules', help='Show rules applied on a specific edge', parents=[kcli_args.logging_args, proof_args]
@@ -324,6 +388,8 @@ def _parse_args(ns: Namespace) -> KMirOpts:
324388
)
325389
case 'gen-spec':
326390
return GenSpecOpts(input_file=Path(ns.input_file), output_file=ns.output_file, start_symbol=ns.start_symbol)
391+
case 'info':
392+
return InfoOpts(smir_file=Path(ns.smir_file), types=ns.types)
327393
case 'prove':
328394
proof_dir = Path(ns.proof_dir)
329395
return ProveRawOpts(
@@ -343,6 +409,11 @@ def _parse_args(ns: Namespace) -> KMirOpts:
343409
full_printer=ns.full_printer,
344410
smir_info=Path(ns.smir_info) if ns.smir_info else None,
345411
omit_current_body=ns.omit_current_body,
412+
nodes=ns.nodes,
413+
node_deltas=ns.node_deltas,
414+
omit_cells=ns.omit_cells,
415+
omit_static_info=ns.omit_static_info,
416+
use_default_printer=ns.use_default_printer,
346417
)
347418
case 'show-rules':
348419
return ShowRulesOpts(

kmir/src/kmir/kprint.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from typing import TYPE_CHECKING
5+
6+
from pyk.kast.pretty import PrettyPrinter, indent
7+
8+
_LOGGER: logging.Logger = logging.getLogger(__name__)
9+
10+
if TYPE_CHECKING:
11+
from pyk.kast.outer import KDefinition
12+
13+
14+
class KMIRPrettyPrinter(PrettyPrinter):
15+
"""
16+
Custom Pretty Printer for improved ListItem display formatting.
17+
Formats ListItem elements with line breaks for better readability.
18+
"""
19+
20+
def __init__(self, definition: KDefinition) -> None:
21+
"""
22+
Initialize KMIR Pretty Printer
23+
24+
Args:
25+
definition: K definition
26+
"""
27+
super().__init__(definition)
28+
self.symbol_table['ListItem'] = lambda *args: 'ListItem (' + (indent(', '.join(args))).strip() + ')\n'

kmir/src/kmir/options.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def __init__(
138138
self,
139139
proof_dir: Path | str,
140140
id: str,
141-
full_printer: bool = True,
141+
full_printer: bool = False,
142142
smir_info: Path | None = None,
143143
omit_current_body: bool = True,
144144
) -> None:
@@ -150,7 +150,48 @@ def __init__(
150150

151151

152152
@dataclass
153-
class ShowOpts(DisplayOpts): ...
153+
class ShowOpts(DisplayOpts):
154+
nodes: tuple[int, ...] | None
155+
node_deltas: tuple[tuple[int, int], ...] | None
156+
omit_cells: tuple[str, ...] | None
157+
omit_static_info: bool
158+
use_default_printer: bool
159+
160+
def __init__(
161+
self,
162+
proof_dir: Path | str,
163+
id: str,
164+
full_printer: bool = False,
165+
smir_info: Path | None = None,
166+
omit_current_body: bool = True,
167+
nodes: str | None = None,
168+
node_deltas: str | None = None,
169+
omit_cells: str | None = None,
170+
omit_static_info: bool = True,
171+
use_default_printer: bool = False,
172+
) -> None:
173+
super().__init__(proof_dir, id, full_printer, smir_info, omit_current_body)
174+
self.omit_static_info = omit_static_info
175+
self.use_default_printer = use_default_printer
176+
self.nodes = tuple(int(n.strip()) for n in nodes.split(',')) if nodes is not None else None
177+
if node_deltas is not None:
178+
deltas = []
179+
for delta in node_deltas.split(','):
180+
parts = delta.strip().split(':')
181+
if len(parts) == 2:
182+
deltas.append((int(parts[0].strip()), int(parts[1].strip())))
183+
self.node_deltas = tuple(deltas)
184+
else:
185+
self.node_deltas = None
186+
187+
static_info_cells = ('<functions>', '<start-symbol>', '<types>', '<adt-to-ty>')
188+
189+
user_omit_cells = tuple(cell.strip() for cell in omit_cells.split(',')) if omit_cells is not None else ()
190+
191+
if omit_static_info:
192+
self.omit_cells = static_info_cells + user_omit_cells
193+
else:
194+
self.omit_cells = user_omit_cells if user_omit_cells else None
154195

155196

156197
@dataclass
@@ -180,6 +221,16 @@ class PruneOpts(ProofOpts):
180221
node_id: int
181222

182223

224+
@dataclass
225+
class InfoOpts(KMirOpts):
226+
smir_file: Path
227+
types: tuple[int, ...] | None
228+
229+
def __init__(self, smir_file: Path, types: str | None = None) -> None:
230+
self.smir_file = smir_file
231+
self.types = tuple(int(t.strip()) for t in types.split(',')) if types is not None else None
232+
233+
183234
@dataclass
184235
class LinkOpts(KMirOpts):
185236
smir_files: list[Path]

0 commit comments

Comments
 (0)