Skip to content

Commit 79fb735

Browse files
ehildenbclaude
andauthored
Enable booster-only simplification via RPC flag, JSON logging on RPC, and standardize log forma (#4919)
~Blocked on: runtimeverification/haskell-backend#4145 ~Blocked on: #4902 This branch adds structured logging infrastructure to the pyk RPC layer to enable downstream log analysis tooling. The motivating use case is correlating simplification calls across worker processes, attributing them to source rules, and diagnosing fixability — none of which is possible without stable, parseable log output and hash-to-source mappings in pyk itself. Changes: - `pyk/kore/rpc`: add `JSON` variant to `KoreExecLogFormat` so execution logs are emitted as one JSON object per line rather than free-form text. The haskell backend already supports json logging. - `pyk/kore/rpc`: log the local port on connect, enabling correlation of log lines to specific worker processes. - All tools: add `%(process)d` to all log format strings for consistent PID tagging across the toolchain. - `pyk/ktool/kprint`: add `KPrint.all_rules` cached property, mapping 12-char rule-hash prefixes to source locations parsed from `allRules.txt`. Downstream consumers use this for getting rule source locations. - `pyk/ktool/kprint`: add in-process `KPrint(..., pyk_print=True)` path, avoiding subprocess overhead when the KAST definition is already loaded. Without the option, current behavior remains default. - `pyk`: surface `booster-only-simplify` flag through `KoreClient` and `CTermSymbolic` (blocked on haskell-backend adding that flag in runtimeverification/haskell-backend#4145). --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0322e11 commit 79fb735

7 files changed

Lines changed: 79 additions & 15 deletions

File tree

pyk/src/pyk/cli/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
T1 = TypeVar('T1')
1717
T2 = TypeVar('T2')
1818

19-
LOG_FORMAT: Final = '%(levelname)s %(asctime)s %(name)s - %(message)s'
19+
LOG_FORMAT: Final = '%(levelname)s %(asctime)s %(name)s [%(process)d] - %(message)s'
2020

2121

2222
def loglevel(args: Namespace) -> int:

pyk/src/pyk/cterm/symbolic.py

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class CTermSymbolic:
7373
_definition: KDefinition
7474
_log_succ_rewrites: bool
7575
_log_fail_rewrites: bool
76+
_booster_only_simplify: bool
7677

7778
def __init__(
7879
self,
@@ -81,11 +82,13 @@ def __init__(
8182
*,
8283
log_succ_rewrites: bool = True,
8384
log_fail_rewrites: bool = False,
85+
booster_only_simplify: bool = False,
8486
):
8587
self._kore_client = kore_client
8688
self._definition = definition
8789
self._log_succ_rewrites = log_succ_rewrites
8890
self._log_fail_rewrites = log_fail_rewrites
91+
self._booster_only_simplify = booster_only_simplify
8992

9093
def kast_to_kore(self, kinner: KInner) -> Pattern:
9194
return kast_to_kore(self._definition, kinner, sort=GENERATED_TOP_CELL)
@@ -100,6 +103,7 @@ def execute(
100103
cut_point_rules: Iterable[str] | None = None,
101104
terminal_rules: Iterable[str] | None = None,
102105
module_name: str | None = None,
106+
booster_only_simplify: bool | None = None,
103107
) -> CTermExecute:
104108

105109
_LOGGER.debug(f'Executing: {cterm}')
@@ -113,6 +117,9 @@ def execute(
113117
module_name=module_name,
114118
log_successful_rewrites=self._log_succ_rewrites,
115119
log_failed_rewrites=self._log_succ_rewrites and self._log_fail_rewrites,
120+
booster_only_simplify=(
121+
booster_only_simplify if booster_only_simplify is not None else self._booster_only_simplify
122+
),
116123
)
117124
except SmtSolverError as err:
118125
raise self._smt_solver_error(err) from err
@@ -143,16 +150,28 @@ def execute(
143150
logs=response.logs,
144151
)
145152

146-
def simplify(self, cterm: CTerm, module_name: str | None = None) -> tuple[CTerm, tuple[LogEntry, ...]]:
153+
def simplify(
154+
self, cterm: CTerm, module_name: str | None = None, booster_only_simplify: bool | None = None
155+
) -> tuple[CTerm, tuple[LogEntry, ...]]:
147156
_LOGGER.debug(f'Simplifying: {cterm}')
148-
kast_simplified, logs = self.kast_simplify(cterm.kast, module_name=module_name)
157+
kast_simplified, logs = self.kast_simplify(
158+
cterm.kast, module_name=module_name, booster_only_simplify=booster_only_simplify
159+
)
149160
return CTerm.from_kast(kast_simplified), logs
150161

151-
def kast_simplify(self, kast: KInner, module_name: str | None = None) -> tuple[KInner, tuple[LogEntry, ...]]:
162+
def kast_simplify(
163+
self, kast: KInner, module_name: str | None = None, booster_only_simplify: bool | None = None
164+
) -> tuple[KInner, tuple[LogEntry, ...]]:
152165
_LOGGER.debug(f'Simplifying: {kast}')
153166
kore = self.kast_to_kore(kast)
154167
try:
155-
kore_simplified, logs = self._kore_client.simplify(kore, module_name=module_name)
168+
kore_simplified, logs = self._kore_client.simplify(
169+
kore,
170+
module_name=module_name,
171+
booster_only_simplify=(
172+
booster_only_simplify if booster_only_simplify is not None else self._booster_only_simplify
173+
),
174+
)
156175
except SmtSolverError as err:
157176
raise self._smt_solver_error(err) from err
158177

@@ -194,6 +213,7 @@ def implies(
194213
failure_reason: bool = False,
195214
module_name: str | None = None,
196215
assume_defined: bool = False,
216+
booster_only_simplify: bool | None = None,
197217
) -> CTermImplies:
198218
_LOGGER.debug(f'Checking implication: {antecedent} #Implies {consequent}')
199219
_consequent = consequent.kast
@@ -211,7 +231,13 @@ def implies(
211231
consequent_kore = self.kast_to_kore(_consequent)
212232
try:
213233
result = self._kore_client.implies(
214-
antecedent_kore, consequent_kore, module_name=module_name, assume_defined=assume_defined
234+
antecedent_kore,
235+
consequent_kore,
236+
module_name=module_name,
237+
assume_defined=assume_defined,
238+
booster_only_simplify=(
239+
booster_only_simplify if booster_only_simplify is not None else self._booster_only_simplify
240+
),
215241
)
216242
except SmtSolverError as err:
217243
raise self._smt_solver_error(err) from err
@@ -231,6 +257,7 @@ def implies(
231257
failure_reason=False,
232258
module_name=module_name,
233259
assume_defined=assume_defined,
260+
booster_only_simplify=booster_only_simplify,
234261
)
235262
config_match = _config_match.csubst
236263
if config_match is None:
@@ -269,11 +296,17 @@ def implies(
269296
csubst = CSubst.from_pred(ml_subst_pred)
270297
return CTermImplies(csubst, (), None, result.logs)
271298

272-
def assume_defined(self, cterm: CTerm, module_name: str | None = None) -> CTerm:
299+
def assume_defined(
300+
self, cterm: CTerm, module_name: str | None = None, booster_only_simplify: bool = False
301+
) -> CTerm:
273302
_LOGGER.debug(f'Computing definedness condition for: {cterm}')
274-
cterm_simplified, logs = self.simplify(cterm, module_name=module_name)
303+
cterm_simplified, logs = self.simplify(
304+
cterm, module_name=module_name, booster_only_simplify=booster_only_simplify
305+
)
275306
kast = KApply(KLabel('#Ceil', [GENERATED_TOP_CELL, GENERATED_TOP_CELL]), [cterm_simplified.config])
276-
kast_simplified, logs = self.kast_simplify(kast, module_name=module_name)
307+
kast_simplified, logs = self.kast_simplify(
308+
kast, module_name=module_name, booster_only_simplify=booster_only_simplify
309+
)
277310
_LOGGER.debug(f'Definedness condition computed: {kast_simplified}')
278311
return cterm.add_constraint(kast_simplified)
279312

@@ -305,6 +338,7 @@ def cterm_symbolic(
305338
log_axioms_file: Path | None = None,
306339
log_succ_rewrites: bool = True,
307340
log_fail_rewrites: bool = False,
341+
booster_only_simplify: bool = False,
308342
start_server: bool = True,
309343
fallback_on: Iterable[FallbackReason] | None = None,
310344
interim_simplification: int | None = None,
@@ -333,7 +367,11 @@ def cterm_symbolic(
333367
) as server:
334368
with KoreClient('localhost', server.port, bug_report=bug_report, bug_report_id=id) as client:
335369
yield CTermSymbolic(
336-
client, definition, log_succ_rewrites=log_succ_rewrites, log_fail_rewrites=log_fail_rewrites
370+
client,
371+
definition,
372+
log_succ_rewrites=log_succ_rewrites,
373+
log_fail_rewrites=log_fail_rewrites,
374+
booster_only_simplify=booster_only_simplify,
337375
)
338376
else:
339377
if port is None:

pyk/src/pyk/kdist/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from typing import Any, Final
1414

1515

16-
LOG_FORMAT: Final = '%(levelname)s %(asctime)s %(name)s - %(message)s'
16+
LOG_FORMAT: Final = '%(levelname)s %(asctime)s %(name)s [%(process)d] - %(message)s'
1717

1818

1919
def package_path(obj: Any) -> Path:

pyk/src/pyk/kore/rpc.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
class KoreExecLogFormat(Enum):
4545
STANDARD = 'standard'
4646
ONELINE = 'oneline'
47+
JSON = 'json'
4748

4849

4950
@final
@@ -119,7 +120,8 @@ def _create_connection(host: str, port: int, timeout: int | None) -> socket.sock
119120
try:
120121
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
121122
sock.connect((host, port))
122-
_LOGGER.info(f'Connected to host: {host}:{port}')
123+
local_port = sock.getsockname()[1]
124+
_LOGGER.info(f'Connected to host: {host}:{port} (local port: {local_port})')
123125
return sock
124126
except ConnectionRefusedError:
125127
sock.close()
@@ -965,6 +967,7 @@ def execute(
965967
module_name: str | None = None,
966968
log_successful_rewrites: bool | None = None,
967969
log_failed_rewrites: bool | None = None,
970+
booster_only_simplify: bool | None = None,
968971
) -> ExecuteResult:
969972
params = filter_none(
970973
{
@@ -978,6 +981,7 @@ def execute(
978981
'state': self._state(pattern),
979982
'log-successful-rewrites': log_successful_rewrites,
980983
'log-failed-rewrites': log_failed_rewrites,
984+
'booster-only': booster_only_simplify,
981985
}
982986
)
983987

@@ -991,13 +995,15 @@ def implies(
991995
*,
992996
module_name: str | None = None,
993997
assume_defined: bool = False,
998+
booster_only_simplify: bool | None = None,
994999
) -> ImpliesResult:
9951000
params = filter_none(
9961001
{
9971002
'antecedent': self._state(antecedent),
9981003
'consequent': self._state(consequent),
9991004
'module': module_name,
10001005
'assume-defined': assume_defined,
1006+
'booster-only': booster_only_simplify,
10011007
}
10021008
)
10031009

@@ -1009,11 +1015,13 @@ def simplify(
10091015
pattern: Pattern,
10101016
*,
10111017
module_name: str | None = None,
1018+
booster_only_simplify: bool | None = None,
10121019
) -> tuple[Pattern, tuple[LogEntry, ...]]:
10131020
params = filter_none(
10141021
{
10151022
'state': self._state(pattern),
10161023
'module': module_name,
1024+
'booster-only': booster_only_simplify,
10171025
}
10181026
)
10191027

pyk/src/pyk/kore_exec_covr/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from typing import Final
1717

1818

19-
_LOG_FORMAT: Final = '%(levelname)s %(name)s - %(message)s'
19+
_LOG_FORMAT: Final = '%(levelname)s %(name)s [%(process)d] - %(message)s'
2020

2121

2222
def do_analyze(definition_dir: Path, input_file: Path) -> None:

pyk/src/pyk/kore_exec_covr/kore_exec_covr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from ..kast.outer import KDefinition, KRule
1616

1717

18-
_LOG_FORMAT: Final = '%(levelname)s %(name)s - %(message)s'
18+
_LOG_FORMAT: Final = '%(levelname)s %(name)s [%(process)d] - %(message)s'
1919
_LOGGER: Final = logging.getLogger(__name__)
2020

2121
_HASKELL_LOG_ENTRY_REGEXP: Final = re.compile(r'(kore-exec|kore-rpc): \[\d*\] Debug \(([a-zA-Z]*)\):(.*)')

pyk/src/pyk/ktool/kprint.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,22 @@ def _temp_file(self, prefix: str | None = None, suffix: str | None = None) -> It
219219
def definition(self) -> KDefinition:
220220
return read_kast_definition(self.definition_dir / 'compiled.json')
221221

222+
@cached_property
223+
def all_rules(self) -> dict[str, str]:
224+
"""Map from 12-char rule hash prefix to source location (file:line:col).
225+
226+
Parsed from allRules.txt in the kompiled definition directory.
227+
Keyed by the first 12 hex characters of each rule's SHA-256 hash.
228+
"""
229+
rules: dict[str, str] = {}
230+
for line in (self.definition_dir / 'allRules.txt').read_text().splitlines():
231+
line = line.strip()
232+
if not line:
233+
continue
234+
full_hash, _, location = line.partition(' ')
235+
rules[full_hash[:12]] = location
236+
return rules
237+
222238
@property
223239
def definition_hash(self) -> str:
224240
return self.definition.hash
@@ -233,7 +249,9 @@ def parse_token(self, ktoken: KToken, *, as_rule: bool = False) -> KInner:
233249
)
234250
return KInner.from_dict(kast_term(json.loads(proc_res.stdout)))
235251

236-
def kore_to_pretty(self, pattern: Pattern) -> str:
252+
def kore_to_pretty(self, pattern: Pattern, *, pyk_print: bool = False) -> str:
253+
if pyk_print:
254+
return self.pretty_print(self.kore_to_kast(pattern))
237255
proc_res = self._expression_kast(
238256
pattern.text,
239257
input=KAstInput.KORE,

0 commit comments

Comments
 (0)