runtimeverification / k

K Framework Tools 7.0
BSD 3-Clause "New" or "Revised" License
444 stars 145 forks source link

K semantic REPL #2925

Open Baltoli opened 1 year ago

Baltoli commented 1 year ago

Current Situation

The debugging tools available for K projects are limited. Currently, the following options are available to developers of K-based projects:

Proposal

We would like to implement a REPL (or a toolkit for building a REPL) for languages implemented in K. Doing so would allow for developers to step through their code at the language (or semantics) level.

Requirements / Features

Initial dump of requirements per @sskeirik:

ehildenb commented 1 year ago

Let's jsut get the first version of k-repl implemented and merged which allows single K steps for now, then we can figure otu the rest.

ehildenb commented 1 year ago

More features it should have:

ehildenb commented 1 year ago

We should also store the nodes (or allow storing the nodes) in a pyk.kcfg.KCFG.

Maybe we add a command store NODE_NAME which allows storing the current node in the KCFG, and defines an alias NODE_NAME for it?

Then the user can use show-cfg to display a textual CFG, and can select another node to jump to with select NODE_NAME.

dwightguth commented 1 year ago

Some questions I want to answer. Some are probably very easy, others will take more time.

I would feel better about getting started on implementing this if we had answers to these questions written down somewhere in a structured manner, whether on Google or in a single issue body

ehildenb commented 1 year ago

The kit REPL that we used for the summarizer had these commands: https://github.com/runtimeverification/ksummarize/blob/master/kit-shell

We also have the commands the kore-repl uses. We should probably pick (roughly) a subset of the intersection of the commands of both.

I would propose:

I recommend these commands because they are what has been useful for the kit-shell for exploring proofs. From the kore-repl, the various things people have used are (i) stepping, (ii) selecting nodes, and (iii) displaying the current explored graph structure, (iv) displaying nodes. All of these functionalities are covered by the above commands.

I recommend pyk for implementation, because it already implements all of these functionalities, and speaks both KAST and Kore, and has direct and fast communication with the backends. It also already has the KCFG datastructure for storing execution graphs and manipulating them, and it has the CTerm abstraction, which defines the operations that must be done quickly over states in order to not cause delay on the Python side. We can make sure that the various manipulations exposed by CTerm (such as CTerm.add_constraint) have fast and direct operations in the backends, so that we can do our executions and manipulations as much as possible purely in the backends.

sskeirik commented 1 year ago

@ehildenb's answer seems to be focused on symbolic execution; concrete debuggers typically have different priorities and I think it is worth highlighting those.

  • If the idea is to interactively enter commands, what commands should we target to start with?

I think the most important commands are (in order of most important to least important):

  1. show [id] - pretty-print configuration of state id (defaults to current configuration)
  2. step [step-count] - executes step-count steps
  3. break <location> - execute semantic steps until location is reached (could be either an input program location or a K rule location --- depending on which one is being debugged)
(Click-to-expand) The commands in this drop-down are all non-essential, nice-to-haves 4. `conditional-break [location] ` - execute semantic steps until predicate holds at location (location defaults to anywhere) 5. `rewind [step-count]` - rewinds `step-count` steps (defaults to 1) 6. `goto ` - sets current configuration to the one with identifier `id` 7. `update ` - updates the configuration to one identical to the previous configuration but with `cell-name` now with value `cell-value`
  • How can we engineer the code for handling commands so that it's extensible to new commands, both user supplied and developer written?
  • How can we support scripting and composition of commands?

I think that this requires developing the correct primitive operations that can be easily composed to produce higher level operations. There are couple of primitives that would be really useful to have:

(Click-to-expand) Implementation details for non-essential commands - (e) serializing a configuration to disk and deserializing it - useful for `rewind`/`goto` - (f) given a map of configuration variables, generate the corresponding initial configuration (or) given a map of cell values, generate the corresponding configuration - needed for `update` plus general programmatic use of debugger - (g) given a configuration and a generic predicate over the configuration, evaluate whether the predicate holds - needed for `conditional-break` Of the items above, implementing the primitives needed for generic `conditional-break` is the hardest. I can think of two general strategies: - (i) develop a generic _functional_ K interpreter (the subset of K with just _function_ rules) that can: read the functional part of the language spec, dynamically load new functions (over specific sorts, cells, or the entire configuration), and dynamically execute those functions - (ii) a rewrite of (parts of) the K stdlib in the same language the debugger is written in (probably Python) so that we can write predicates in debugger lang to execute over a K configuration as represented in the debugger While I don't think a generic `conditional-break` should be a short-term priority, I think this is a goal worth pursuing in the long-term.
  • How does potentially executing multiple branches in parallel interact with the interactivity of entering commands?

It seems like you either:

dwightguth commented 1 year ago

While all of these commands are potentially valuable, I want to remind everyone that what I'm trying to get us to agree on is a basic initial version of the tool. Please moderate your expectations because it's inevitable not everything from these lists will make it into the first version prototype.

sskeirik commented 1 year ago

To try and make priority for a concrete debugger more clear, I have added click-to-expand sections to hide non-essential commands and their implementation details to my previous comment. I also added two other primitives to my previous comment that are needed for break.

tothtamas28 commented 1 year ago

We would like to implement a REPL (or a toolkit for building a REPL) for languages implemented in K.

I think the focus should be on the second option. So the backends would expose a common debugging interface (with additional custom features that the particular backend supports) for which we can implement a client in Python. The simplest K REPL is then a small script that starts and initializes a Python interpreter.


In this setting, the following questions by @dwightguth would (to some extent) be addressed:

Are we set on python as the language to build this in?

For the client code, yes. The server interface has to be implemented by the backends.

Do we want a command language and if so, what should its features be? How can we support scripting and composition of commands? How can we engineer the code for handling commands so that it's extensible to new commands, both user supplied and developer written?

The scripting language is Python, with all the power (and complexity) it comes with.

Do we want auto complete and other line editing support?

The Python interpreter provides this out of the box.

Do the tools already exist to implement each command? If not, what is missing?

For the client, most of what we need for an MVP is already implemented in pyk.

How does potentially executing multiple branches in parallel interact with the interactivity of entering commands?

We can use asyncio or some other Python library to handle async calls in the client.


The following questions regarding interface design would still be open:

If the idea is to interactively enter commands, what commands should we target to start with? For each command we are planning initially, what should the parameters and output of the command be?

ehildenb commented 1 year ago

Commands:

Notes:

Baltoli commented 1 year ago

https://github.com/runtimeverification/llvm-backend/blob/pybind/bin/k-repl

Note that the API as installed is slightly different to this demo as well.

Baltoli commented 1 year ago

Steps to take:

ehildenb commented 1 year ago

Notes:

Remember our users of k-repl:

Some example use-cases: