Skip to content

Commit 3e27c66

Browse files
committed
Stepping in bytecode
It should be possible to get sources, but saving them would be kind of involved so I'm not so sure about that one. Also we really ought to get arguments in native compiled functions.
1 parent 3c89efd commit 3e27c66

File tree

10 files changed

+180
-64
lines changed

10 files changed

+180
-64
lines changed

include/clasp/core/step.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
3+
#include <clasp/core/foundation.h>
4+
#include <clasp/core/object.h>
5+
#include <clasp/core/lisp.h>
6+
7+
namespace core {
8+
9+
void breakstep(T_sp source, void* frame);
10+
void breakstep_args(void* frame, Function_sp fun, List_sp args);
11+
12+
}; // namespace core

src/core/bytecode.cc

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <clasp/core/designators.h> // calledFunctionDesignator
1717
#include <clasp/core/evaluator.h> // eval::funcall
1818
#include <clasp/gctools/interrupt.h> // handle_all_queued_interrupts
19+
#include <clasp/core/step.h> // breakstep_arguments
1920

2021
#define VM_CODES
2122
#include <virtualMachine.h>
@@ -60,6 +61,19 @@ void BytecodeModule_O::register_for_debug() {
6061
newc->setCdr(old);
6162
}
6263

64+
// Note that we check stepping in the callER not the callEE.
65+
// This is so that we could provide the actual source forms, as we already do
66+
// in native code. TODO
67+
static void maybe_step_call(void* frame,
68+
Function_sp func, size_t nargs, T_O** rargs) {
69+
if (my_thread->_Breakstep) [[unlikely]] {
70+
ql::list args;
71+
for (size_t iarg = 0; iarg < nargs; ++iarg)
72+
args << T_sp((gctools::Tagged)rargs[iarg]);
73+
breakstep_args(frame, func, args.cons());
74+
}
75+
}
76+
6377
static inline int16_t read_s16(unsigned char* pc) {
6478
uint8_t byte0 = *pc;
6579
uint8_t byte1 = *(pc + 1);
@@ -259,6 +273,7 @@ bytecode_vm(VirtualMachine& vm, T_O** literals, T_O** closed, Closure_O* closure
259273
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
260274
Function_sp func = gc::As_assert<Function_sp>(tfunc);
261275
T_O** args = vm.stackref(sp, nargs - 1);
276+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
262277
// We push the PC for the debugger (see make_bytecode_frame in backtrace.cc)
263278
// We do this here rather than bytecode_call because e.g. we may call a
264279
// non-bytecode function, that in turn calls a bunch of different bytecode
@@ -281,6 +296,7 @@ bytecode_vm(VirtualMachine& vm, T_O** literals, T_O** closed, Closure_O* closure
281296
VM_RECORD_PLAYBACK(func, "vm_call_receive_one_func");
282297
VM_RECORD_PLAYBACK((void*)(uintptr_t)nargs, "vm_call_receive_one_nargs");
283298
T_O** args = vm.stackref(sp, nargs - 1);
299+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
284300
#if DEBUG_VM_RECORD_PLAYBACK == 1
285301
for (size_t ii = 0; ii < nargs; ii++) {
286302
stringstream name_args;
@@ -305,6 +321,7 @@ bytecode_vm(VirtualMachine& vm, T_O** literals, T_O** closed, Closure_O* closure
305321
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
306322
Function_sp func = gc::As_assert<Function_sp>(tfunc);
307323
T_O** args = vm.stackref(sp, nargs - 1);
324+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
308325
vm.push(sp, (T_O*)pc);
309326
vm._pc = pc;
310327
vm._stackPointer = sp;
@@ -658,6 +675,7 @@ bytecode_vm(VirtualMachine& vm, T_O** literals, T_O** closed, Closure_O* closure
658675
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
659676
Function_sp func = gc::As_assert<Function_sp>(tfunc);
660677
T_O** args = vm.stackref(sp, nargs - 1);
678+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
661679
vm.push(sp, (T_O*)pc);
662680
vm._pc = pc;
663681
vm._stackPointer = sp;
@@ -675,6 +693,7 @@ bytecode_vm(VirtualMachine& vm, T_O** literals, T_O** closed, Closure_O* closure
675693
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
676694
Function_sp func = gc::As_assert<Function_sp>(tfunc);
677695
T_O** args = vm.stackref(sp, nargs - 1);
696+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
678697
vm.push(sp, (T_O*)pc);
679698
vm._pc = pc;
680699
vm._stackPointer = sp;
@@ -693,6 +712,7 @@ bytecode_vm(VirtualMachine& vm, T_O** literals, T_O** closed, Closure_O* closure
693712
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
694713
Function_sp func = gc::As_assert<Function_sp>(tfunc);
695714
T_O** args = vm.stackref(sp, nargs - 1);
715+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
696716
vm.push(sp, (T_O*)pc);
697717
vm._pc = pc;
698718
vm._stackPointer = sp;
@@ -1045,6 +1065,7 @@ static unsigned char* long_dispatch(VirtualMachine& vm, unsigned char* pc, Multi
10451065
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
10461066
Function_sp func = gc::As_assert<Function_sp>(tfunc);
10471067
T_O** args = vm.stackref(sp, nargs - 1);
1068+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
10481069
vm.push(sp, (T_O*)pc);
10491070
vm._pc = pc;
10501071
vm._stackPointer = sp;
@@ -1060,9 +1081,10 @@ static unsigned char* long_dispatch(VirtualMachine& vm, unsigned char* pc, Multi
10601081
DBG_VM1("long call-receive-one %" PRIu16 "\n", nargs);
10611082
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
10621083
Function_sp func = gc::As_assert<Function_sp>(tfunc);
1084+
T_O** args = vm.stackref(sp, nargs - 1);
1085+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
10631086
VM_RECORD_PLAYBACK(func, "vm_call_receive_one_func");
10641087
VM_RECORD_PLAYBACK((void*)(uintptr_t)nargs, "vm_call_receive_one_nargs");
1065-
T_O** args = vm.stackref(sp, nargs - 1);
10661088
#if DEBUG_VM_RECORD_PLAYBACK == 1
10671089
for (size_t ii = 0; ii < nargs; ii++) {
10681090
stringstream name_args;
@@ -1089,6 +1111,7 @@ static unsigned char* long_dispatch(VirtualMachine& vm, unsigned char* pc, Multi
10891111
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
10901112
Function_sp func = gc::As_assert<Function_sp>(tfunc);
10911113
T_O** args = vm.stackref(sp, nargs - 1);
1114+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
10921115
vm.push(sp, (T_O*)pc);
10931116
vm._pc = pc;
10941117
vm._stackPointer = sp;
@@ -1307,6 +1330,7 @@ static unsigned char* long_dispatch(VirtualMachine& vm, unsigned char* pc, Multi
13071330
T_sp tfunc((gctools::Tagged)(*(vm.stackref(sp, nargs))));
13081331
Function_sp func = gc::As_assert<Function_sp>(tfunc);
13091332
T_O** args = vm.stackref(sp, nargs - 1);
1333+
maybe_step_call(__builtin_frame_address(0), func, nargs, args);
13101334
vm.push(sp, (T_O*)pc);
13111335
vm._pc = pc;
13121336
vm._stackPointer = sp;

src/core/corePackage.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ SYMBOL_EXPORT_SC_(CorePkg, _PLUS_fe_overflow_PLUS_);
211211
SYMBOL_EXPORT_SC_(CorePkg, arguments);
212212
SYMBOL_EXPORT_SC_(CorePkg, array_out_of_bounds);
213213
SYMBOL_EXPORT_SC_(CorePkg, breakstep);
214+
SYMBOL_EXPORT_SC_(CorePkg, breakstep_arguments);
214215
SYMBOL_EXPORT_SC_(CorePkg, c_local);
215216
SYMBOL_EXPORT_SC_(CorePkg, circle_subst);
216217
SYMBOL_EXPORT_SC_(CorePkg, class_source_location)

src/core/cscript.lisp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#~"stackmap.cc"
1818
#~"debugger.cc"
1919
#~"debugger2.cc"
20+
#~"step.cc"
2021
#~"backtrace.cc"
2122
#~"bytecode.cc"
2223
#~"bytecode_compiler.cc"

src/core/debugger2.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <clasp/core/lispStream.h>
1414
#include <clasp/core/wrappers.h>
1515
#include <clasp/core/backtrace.h>
16+
#include <clasp/gctools/threadlocal.h> // ThreadLocalState access
1617

1718
namespace core {
1819

src/core/step.cc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include <clasp/core/foundation.h>
2+
#include <clasp/core/object.h>
3+
#include <clasp/core/lisp.h>
4+
#include <clasp/core/evaluator.h>
5+
#include <clasp/gctools/threadlocal.h>
6+
7+
namespace core {
8+
9+
// RAII thing to toggle breakstep while respecting nonlocal exit.
10+
// TODO: Check that this works with our unwinding? Not sure it does
11+
struct BreakstepToggle {
12+
ThreadLocalState* mthread;
13+
bool old_breakstep;
14+
BreakstepToggle(ThreadLocalState* thread, bool new_breakstep) {
15+
mthread = thread;
16+
old_breakstep = thread->_Breakstep;
17+
thread->_Breakstep = new_breakstep;
18+
}
19+
~BreakstepToggle() { mthread->_Breakstep = old_breakstep; }
20+
};
21+
22+
void breakstep(T_sp source, void* frame) {
23+
void* bframe = my_thread->_BreakstepFrame;
24+
// If bframe is NULL, we are doing step-into.
25+
// Otherwise, we are doing step-over, and we need to check
26+
// if we've returned yet. bframe is the frame step-over was initiated
27+
// from, and lframe/frame is the caller frame.
28+
// We have to check here because a function being stepped over may
29+
// nonlocally exit past the caller, and in that situation we want to
30+
// resume stepping.
31+
// FIXME: We assume stack growth direction here.
32+
if (!bframe || (frame >= bframe)) {
33+
// Make sure we don't invoke the stepper recursively,
34+
// but can do so again once we're out of the Lisp interaction.
35+
BreakstepToggle tog(my_thread, false);
36+
T_sp res = core::eval::funcall(core::_sym_breakstep, source);
37+
if (res.fixnump()) {
38+
switch (res.unsafe_fixnum()) {
39+
case 0:
40+
goto stop_stepping;
41+
case 1:
42+
my_thread->_BreakstepFrame = NULL;
43+
return;
44+
case 2:
45+
my_thread->_BreakstepFrame = frame;
46+
return;
47+
}
48+
}
49+
SIMPLE_ERROR("BUG: Unknown return value from {}: {}", _rep_(core::_sym_breakstep), _rep_(res));
50+
} else
51+
return;
52+
stop_stepping: // outside the scope of tog
53+
my_thread->_Breakstep = false;
54+
return;
55+
}
56+
57+
// when we have a call but no source - bytecode for now FIXME
58+
void breakstep_args(void* frame, Function_sp function, List_sp args) {
59+
void* bframe = my_thread->_BreakstepFrame;
60+
// FIXME: We assume stack growth direction here.
61+
if (!bframe || (frame >= bframe)) {
62+
// Make sure we don't invoke the stepper recursively,
63+
// but can do so again once we're out of the Lisp interaction.
64+
BreakstepToggle tog(my_thread, false);
65+
T_sp res = core::eval::funcall(core::_sym_breakstep_arguments,
66+
function, args);
67+
if (res.fixnump()) {
68+
switch (res.unsafe_fixnum()) {
69+
case 0:
70+
goto stop_stepping;
71+
case 1:
72+
my_thread->_BreakstepFrame = NULL;
73+
return;
74+
case 2:
75+
my_thread->_BreakstepFrame = frame;
76+
return;
77+
}
78+
}
79+
SIMPLE_ERROR("BUG: Unknown return value from {}: {}", _rep_(core::_sym_breakstep), _rep_(res));
80+
} else
81+
return;
82+
stop_stepping: // outside the scope of tog
83+
my_thread->_Breakstep = false;
84+
return;
85+
}
86+
87+
}; // namespace core

src/lisp/kernel/clos/conditions.lisp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -493,14 +493,13 @@ format string."
493493
:FORMAT-ARGUMENTS format-arguments)))))
494494
nil)
495495

496-
(defun breakstep (source)
496+
(defun %breakstep (condition)
497497
"Pause due to stepping or a breakpoint."
498498
(clasp-debug:with-truncated-stack ()
499499
(restart-case
500500
(let ((*debugger-hook* nil))
501-
(invoke-debugger
502-
(make-condition 'clasp-debug:step-form :source source)))
503-
;; cc_breakstep interprets our return value as follows:
501+
(invoke-debugger condition))
502+
;; the C++ breakstep interprets our return value as follows:
504503
;; 0: continue without stepping
505504
;; 1: step-into
506505
;; 2: step-over
@@ -509,12 +508,21 @@ format string."
509508
:report "Resume normal, unstepped execution."
510509
0)
511510
(clasp-debug:step-into ()
512-
:report "Step into call."
511+
:report "Step into form."
513512
1)
514513
(clasp-debug:step-over ()
515-
:report "Step over call."
514+
:report "Step over form."
516515
2))))
517516

517+
;;; called from C++ - see step.cc
518+
(defun breakstep (source)
519+
(%breakstep (make-condition 'clasp-debug:step-form :source source)))
520+
(defun breakstep-arguments (function arguments &optional source)
521+
(%breakstep (make-condition 'clasp-debug:step-call
522+
:source source
523+
:function function :arguments arguments
524+
:arguments-available-p t)))
525+
518526
(defun warn (datum &rest arguments)
519527
"Args: (format-string &rest args)
520528
Formats FORMAT-STRING and ARGs to *ERROR-OUTPUT* as a warning message. Enters
@@ -1255,12 +1263,32 @@ The conflict resolver must be one of ~s" chosen-symbol candidates))
12551263
for value in values
12561264
collect (assert-prompt place-name value)))))))
12571265

1258-
(define-condition step-condition () ())
1266+
(define-condition clasp-debug:step-condition () ())
12591267

1260-
(define-condition clasp-debug:step-form (step-condition)
1268+
(define-condition clasp-debug:step-form (clasp-debug:step-condition)
12611269
((%source :initarg :source :reader source))
12621270
(:report (lambda (condition stream)
1263-
(format stream "Evaluating form: ~s" (source condition)))))
1271+
(format stream "Evaluating form:~%~t~s" (source condition)))))
1272+
1273+
(define-condition clasp-debug:step-call (clasp-debug:step-condition)
1274+
((%source :initarg :source :reader source)
1275+
(%called-function :initarg :function :reader called-function)
1276+
(%arguments :initarg :arguments :reader arguments)
1277+
(%arguments-available-p :initarg :arguments-available-p
1278+
:reader arguments-available-p))
1279+
(:report (lambda (condition stream)
1280+
(let ((form (source condition)))
1281+
(if form ; NIL is never really a call
1282+
(format stream "Evaluating form:~%~t~s" form)
1283+
(let* ((function (called-function condition))
1284+
(name (and function (core:function-name function)))
1285+
(dname (if (eq name 'cl:lambda)
1286+
"anonymous function"
1287+
name)))
1288+
(format stream "Calling ~a" name))))
1289+
(when (arguments-available-p condition)
1290+
(format stream "~%With arguments:~%~t~s"
1291+
(arguments condition))))))
12641292

12651293
;;; ----------------------------------------------------------------------
12661294
;;; Unicode, initially forgotten in clasp

src/lisp/kernel/lsp/debug.lisp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
;; misc
4444
(%export '(#:function-name-package))
4545
;; stepper
46-
(%export '(#:step-form #:step-into #:step-over))
46+
(%export '(#:step-condition #:step-form #:step-call
47+
#:step-into #:step-over))
4748
(import '(core:set-breakstep core:unset-breakstep core:breakstepping-p))
4849
(export '(core:set-breakstep core:unset-breakstep core:breakstepping-p))))
4950

src/lisp/regression-tests/debug.lisp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -264,23 +264,27 @@
264264
(let ((ext:*invoke-debugger-hook*
265265
(lambda (condition old-hook)
266266
(declare (ignore old-hook))
267-
(return (typep condition 'clasp-debug:step-form)))))
267+
(return (typep condition 'clasp-debug:step-condition)))))
268268
(step (print 4)))))
269269

270270
(test breakstepping-p
271-
(values (progn (clasp-debug:set-breakstep)
272-
(clasp-debug:breakstepping-p))
273-
(progn (clasp-debug:unset-breakstep)
274-
(clasp-debug:breakstepping-p)))
275-
(t nil))
271+
(let ((ext:*invoke-debugger-hook* ; don't step during the test!
272+
(lambda (condition old-hook)
273+
(declare (ignore condition old-hook))
274+
(invoke-restart 'clasp-debug:step-over))))
275+
(values (progn (clasp-debug:set-breakstep)
276+
(clasp-debug:breakstepping-p))
277+
(progn (clasp-debug:unset-breakstep)
278+
(clasp-debug:breakstepping-p))))
279+
(t nil))
276280

277281
;;; breakstep can also be used to enable the stepper, without STEP itself.
278282
(test-true breakstep
279283
(block nil
280284
(let ((ext:*invoke-debugger-hook*
281285
(lambda (condition old-hook)
282286
(declare (ignore old-hook))
283-
(return (typep condition 'clasp-debug:step-form)))))
287+
(return (typep condition 'clasp-debug:step-condition)))))
284288
(clasp-debug:set-breakstep)
285289
(unwind-protect
286290
(locally

0 commit comments

Comments
 (0)