Skip to content
This repository has been archived by the owner on Feb 17, 2024. It is now read-only.

timemachine: interactive debugger #23

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3fa96c4
Get the REPL going
chriso May 27, 2023
240f1b0
Add to run command
chriso May 27, 2023
373ba0d
Start the REPL before the module
chriso May 27, 2023
c49aa7c
Merge branch 'main' into debug-repl
chriso May 27, 2023
a26c9e1
Merge branch 'main' into debug-repl
chriso May 27, 2023
013d747
Handle the abort case
chriso May 27, 2023
f90e909
Clarify the module-level events
chriso May 27, 2023
150e931
Detect WASI extensions automatically
chriso May 27, 2023
49331dc
Add some basic commands
chriso May 27, 2023
e144154
Match lldb help
chriso May 27, 2023
cda9574
Merge branch 'main' into debug-repl
chriso May 28, 2023
2512543
Fix listener in the run command
chriso May 29, 2023
f67874e
Merge branch 'main' into debug-repl
chriso May 29, 2023
d21775f
Merge branch 'main' into debug-repl
chriso May 30, 2023
48248e4
Merge branch 'main' into debug-repl
chriso May 31, 2023
d7174c2
Play around with color and tracing
chriso May 31, 2023
6fb508c
Merge branch 'main' into debug-repl
chriso May 31, 2023
a39b7a8
Support restarting
chriso Jun 1, 2023
16703c8
Rearrange commands
chriso Jun 1, 2023
f998867
Step vs. continue
chriso Jun 1, 2023
86d4592
We no longer need this
chriso Jun 1, 2023
e5e906d
Fix merge thing
chriso Jun 1, 2023
8653751
Slightly better handling of SIGINT
chriso Jun 1, 2023
04b96f7
Play around with different tracer options
chriso Jun 1, 2023
411c1ac
Add rudimentary breakpoints
chriso Jun 1, 2023
1eba462
Break on errnos
chriso Jun 1, 2023
da7cd4b
Merge branch 'main' into debug-repl
chriso Jun 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/stealthrocket/timecraft
go 1.20

require (
github.com/fatih/color v1.15.0
github.com/google/flatbuffers v23.5.26+incompatible
github.com/google/go-cmp v0.5.9
github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3
Expand All @@ -16,4 +17,8 @@ require (
gopkg.in/yaml.v3 v3.0.1
)

require golang.org/x/sys v0.8.0 // indirect
require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
golang.org/x/sys v0.8.0 // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg=
github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
Expand All @@ -8,6 +10,11 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/stealthrocket/wasi-go v0.3.0 h1:+L+DfnHkBmOkUmCbsyO9j1KPqsAFu426jD3HH/7BABc=
github.com/stealthrocket/wasi-go v0.3.0/go.mod h1:LBhZHvAroNNQTejkVTMJZ01ssj3jXF+3Lkbru4cTzGQ=
github.com/stealthrocket/wazergo v0.19.0 h1:0ZBya2fBURvV+I2hGl0vcuQ8dgoUvllxQ7aYlZSA5nI=
Expand All @@ -18,6 +25,8 @@ github.com/tetratelabs/wazero v1.1.1-0.20230522055633-256b7a4bf970 h1:X5OOeHRjoL
github.com/tetratelabs/wazero v1.1.1-0.20230522055633-256b7a4bf970/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
75 changes: 75 additions & 0 deletions internal/debug/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package debug

import (
"github.com/stealthrocket/timecraft/internal/timemachine/wasicall"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
)

// Event is an event generated during execution of a WebAssembly module.
type Event interface {
event()
}

// ModuleBeforeEvent is an event that's triggered before a module is executed.
type ModuleBeforeEvent struct {
Module wazero.CompiledModule
}

func (*ModuleBeforeEvent) event() {}

// ModuleAfterEvent is an event that's triggered after a module has been
// executed.
type ModuleAfterEvent struct {
Error any
}

func (*ModuleAfterEvent) event() {}

// FunctionCallBeforeEvent is an event that's triggered before a function is
// called in a WebAssembly module.
type FunctionCallBeforeEvent struct {
Module api.Module
Function api.FunctionDefinition
Params []uint64
StackIterator experimental.StackIterator
}

func (*FunctionCallBeforeEvent) event() {}

// FunctionCallAfterEvent is an event that's triggered after a function is
// called in a WebAssembly module.
type FunctionCallAfterEvent struct {
Module api.Module
Function api.FunctionDefinition
Results []uint64
}

func (*FunctionCallAfterEvent) event() {}

// FunctionCallAbortEvent is an event that's triggered when a function call is
// aborted in a WebAssembly module.
type FunctionCallAbortEvent struct {
Module api.Module
Function api.FunctionDefinition
Error error
}

func (*FunctionCallAbortEvent) event() {}

// SystemCallBeforeEvent is an event that's triggered before a WASI host
// function is called in a WebAssembly module.
type SystemCallBeforeEvent struct {
wasicall.Syscall
}

func (*SystemCallBeforeEvent) event() {}

// SystemCallAfterEvent is an event that's triggered before a WASI host
// function is called in a WebAssembly module.
type SystemCallAfterEvent struct {
wasicall.Syscall
}

func (*SystemCallAfterEvent) event() {}
93 changes: 93 additions & 0 deletions internal/debug/listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package debug

import (
"context"

"github.com/stealthrocket/timecraft/internal/timemachine/wasicall"
"github.com/stealthrocket/wasi-go"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
)

// Listener is an Event listener.
//
// The Listener exists to bridge the gap between wazero's low-level
// experimental.FunctionListener and the higher-level wasi.System interface.
// The Listener can be connected to either or both sources of events and can
// be used to drive interactive debugging experiences. The design also allows
// for additional types of Event to be added in future, such as events
// triggered when doing instruction or line level stepping.
type Listener interface {
// OnEvent is an Event handler.
//
// The Event and its fields must not be retained across calls.
OnEvent(context.Context, Event)
}

// RegisterFunctionListener registers an experimental.FunctionListener that
// generates FunctionCallBeforeEvent and FunctionCallAfterEvent events for the provided
// Listener.
//
// The listener must be registered before the WebAssembly module is compiled.
func RegisterFunctionListener(ctx context.Context, listener Listener) context.Context {
return context.WithValue(ctx,
experimental.FunctionListenerFactoryKey{},
NewFunctionListenerFactory(listener),
)
}

// NewFunctionListenerFactory creates a experimental.FunctionListenerFactory
// that builds an experimental.FunctionListener that generates
// FunctionCallBeforeEvent and FunctionCallAfterEvent events for the provided
// Listener.
func NewFunctionListenerFactory(listener Listener) experimental.FunctionListenerFactory {
return &functionListenerFactory{listener}
}

type functionListenerFactory struct {
Listener
}

func (c *functionListenerFactory) NewFunctionListener(definition api.FunctionDefinition) experimental.FunctionListener {
return &functionListener{Listener: c.Listener}
}

type functionListener struct {
Listener

// cached events
before FunctionCallBeforeEvent
after FunctionCallAfterEvent
abort FunctionCallAbortEvent
}

func (l *functionListener) Before(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, stackIterator experimental.StackIterator) {
l.before = FunctionCallBeforeEvent{mod, def, params, stackIterator}
l.Listener.OnEvent(ctx, &l.before)
}

func (l *functionListener) After(ctx context.Context, mod api.Module, def api.FunctionDefinition, results []uint64) {
l.after = FunctionCallAfterEvent{mod, def, results}
l.Listener.OnEvent(ctx, &l.after)
}

func (l *functionListener) Abort(ctx context.Context, mod api.Module, def api.FunctionDefinition, err error) {
l.abort = FunctionCallAbortEvent{mod, def, err}
l.Listener.OnEvent(ctx, &l.abort)
}

// WASIListener wraps a wasi.System to generate SystemCallBeforeEvent and
// SystemCallAfterEvent events for the provided Listener.
func WASIListener(system wasi.System, listener Listener) wasi.System {
var before SystemCallBeforeEvent
var after SystemCallAfterEvent
return wasicall.NewObserver(system,
func(ctx context.Context, syscall wasicall.Syscall) {
before = SystemCallBeforeEvent{syscall}
listener.OnEvent(ctx, &before)
},
func(ctx context.Context, syscall wasicall.Syscall) {
after = SystemCallAfterEvent{syscall}
listener.OnEvent(ctx, &after)
})
}
Loading
Loading