Skip to content

Commit c602566

Browse files
authored
πŸš€ Add checkpointing policy (#253)
✨ adds checkpointing policy (for multiverse debugging) ✨ adds the "continue for" debug operation **commits:** * Add basic checkpointing snapshotting policy A checkpoint is taken at primitives and every x instructions. When the VM is paused it also makes a checkpoint so the debugger knows where the other snapshots are relative to the current point in time and so it can also display what the current state of the vm is. TODO: Will need to be extended a bit when using multiverse debugging. Choicepoints should have checkpoints right after reading sensor values so you don't have to remember the sensor value. * Remove unused duplicate interpret function * Add instructions_executed into a json object * Checkpoint on step so the debugger knows the current state again + checkpoint after instead of before primitive calls * Added continue for debug interrupt that allows executing x instructions of the program * Take a checkpoint upon enabling checkpointing mode * Fix uninitialized variable * No checkpointing during WARDUINOinit * Only send STEP! after actually completing a step and not before Also send any checkpoints right before the STEP!. * Allow setting the checkpoint interval instead of hardcoding it to 10 * Make checkpoint interval a 32bit integer * Send arguments + primitive index that was called in checkpoint messages * Got rid of unneeded use of prev_pc_ptr * Clang-format * Use PRIu32 to display amount of instructions to step in continue for debug message * Rename isPrimitiveBeingCalled and remove unnecessary Module argument * Minor cleanup * Clang-format * Use raw string literal * Fix checkpointing so checkpoints are only taken after primitive calls
1 parent 35bdf7b commit c602566

File tree

5 files changed

+160
-17
lines changed

5 files changed

+160
-17
lines changed

β€Žplatforms/Zephyr/prj.confβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ CONFIG_CONSOLE_GETCHAR_BUFSIZE=4096
2929
CONFIG_CONSOLE_PUTCHAR_BUFSIZE=4096
3030

3131
CONFIG_POSIX_API=y
32-
CONFIG_MAIN_STACK_SIZE=4096
32+
CONFIG_MAIN_STACK_SIZE=8192

β€Žsrc/Debug/debugger.cppβ€Ž

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ Debugger::Debugger(Channel *duplex) {
2222
this->channel = duplex;
2323
this->supervisor_mutex = new warduino::mutex();
2424
this->supervisor_mutex->lock();
25-
this->asyncSnapshots = false;
25+
this->snapshotPolicy = SnapshotPolicy::none;
26+
this->checkpointInterval = 10;
27+
this->instructions_executed = 0;
28+
this->fidx_called = {};
29+
this->remaining_instructions = -1;
2630
}
2731

2832
// Public methods
@@ -190,6 +194,12 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) {
190194
exit(0);
191195
case interruptPAUSE:
192196
this->pauseRuntime(m);
197+
// Make a checkpoint so the debugger knows the current state and
198+
// knows how many instructions were executed since the last
199+
// checkpoint.
200+
if (snapshotPolicy == SnapshotPolicy::checkpointing) {
201+
checkpoint(m, true);
202+
}
193203
this->channel->write("PAUSE!\n");
194204
free(interruptData);
195205
break;
@@ -206,6 +216,15 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) {
206216
this->handleInterruptBP(m, interruptData);
207217
free(interruptData);
208218
break;
219+
case interruptContinueFor: {
220+
uint8_t *data = interruptData + 1;
221+
uint32_t amount = read_B32(&data);
222+
debug("Continue for %" PRIu32 " instruction(s)\n", amount);
223+
remaining_instructions = (int32_t)amount;
224+
*program_state = WARDUINOrun;
225+
free(interruptData);
226+
break;
227+
}
209228
case interruptDUMP:
210229
this->pauseRuntime(m);
211230
this->dump(m);
@@ -259,16 +278,18 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) {
259278
this->pauseRuntime(m);
260279
free(interruptData);
261280
snapshot(m);
281+
this->channel->write("\n");
262282
break;
263-
case interruptEnableSnapshots:
264-
enableSnapshots(interruptData + 1);
283+
case interruptSetSnapshotPolicy:
284+
setSnapshotPolicy(m, interruptData + 1);
265285
free(interruptData);
266286
break;
267287
case interruptInspect: {
268288
uint8_t *data = interruptData + 1;
269289
uint16_t numberBytes = read_B16(&data);
270290
uint8_t *state = interruptData + 3;
271291
inspect(m, numberBytes, state);
292+
this->channel->write("\n");
272293
free(interruptData);
273294
break;
274295
}
@@ -429,7 +450,6 @@ void Debugger::handleInterruptRUN(const Module *m,
429450
}
430451

431452
void Debugger::handleSTEP(const Module *m, RunningState *program_state) {
432-
this->channel->write("STEP!\n");
433453
*program_state = WARDUINOstep;
434454
this->skipBreakpoint = m->pc_ptr;
435455
}
@@ -918,18 +938,82 @@ void Debugger::inspect(Module *m, const uint16_t sizeStateArray,
918938
}
919939
}
920940
}
921-
this->channel->write("}\n");
941+
this->channel->write("}");
942+
}
943+
944+
void Debugger::setSnapshotPolicy(Module *m, uint8_t *interruptData) {
945+
snapshotPolicy = SnapshotPolicy{*interruptData};
946+
947+
// Make a checkpoint when you first enable checkpointing
948+
if (snapshotPolicy == SnapshotPolicy::checkpointing) {
949+
uint8_t *ptr = interruptData + 1;
950+
checkpointInterval = read_B32(&ptr);
951+
checkpoint(m, true);
952+
}
922953
}
923954

924-
void Debugger::enableSnapshots(const uint8_t *interruptData) {
925-
asyncSnapshots = *interruptData;
955+
std::optional<uint32_t> getPrimitiveBeingCalled(Module *m, uint8_t *pc_ptr) {
956+
if (!pc_ptr) {
957+
return {};
958+
}
959+
960+
// TODO: Support call_indirect
961+
uint8_t opcode = *pc_ptr;
962+
if (opcode == 0x10) { // call opcode
963+
uint8_t *pc_copy = pc_ptr + 1;
964+
uint32_t fidx = read_LEB_32(&pc_copy);
965+
if (fidx < m->import_count) {
966+
return fidx;
967+
}
968+
}
969+
return {};
926970
}
927971

928-
void Debugger::sendAsyncSnapshots(Module *m) const {
929-
if (asyncSnapshots) {
972+
void Debugger::handleSnapshotPolicy(Module *m) {
973+
if (snapshotPolicy == SnapshotPolicy::atEveryInstruction) {
930974
this->channel->write("SNAPSHOT ");
931975
snapshot(m);
976+
this->channel->write("\n");
977+
} else if (snapshotPolicy == SnapshotPolicy::checkpointing) {
978+
if (instructions_executed >= checkpointInterval || fidx_called) {
979+
checkpoint(m);
980+
}
981+
instructions_executed++;
982+
983+
// Store arguments of last primitive call.
984+
if ((fidx_called = getPrimitiveBeingCalled(m, m->pc_ptr))) {
985+
const Type *type = m->functions[*fidx_called].type;
986+
for (uint32_t i = 0; i < type->param_count; i++) {
987+
prim_args[type->param_count - i - 1] =
988+
m->stack[m->sp - i].value.uint32;
989+
}
990+
}
991+
} else if (snapshotPolicy != SnapshotPolicy::none) {
992+
this->channel->write("WARNING: Invalid snapshot policy.");
993+
}
994+
}
995+
996+
void Debugger::checkpoint(Module *m, bool force) {
997+
if (instructions_executed == 0 && !force) {
998+
return;
932999
}
1000+
1001+
this->channel->write(R"(CHECKPOINT {"instructions_executed": %d, )",
1002+
instructions_executed);
1003+
if (fidx_called) {
1004+
this->channel->write(R"("fidx_called": %d, "args": [)", *fidx_called);
1005+
const Block &func_block = m->functions[*fidx_called];
1006+
bool comma = false;
1007+
for (uint32_t i = 0; i < func_block.type->param_count; i++) {
1008+
channel->write("%s%d", comma ? ", " : "", prim_args[i]);
1009+
comma = true;
1010+
}
1011+
this->channel->write("], ");
1012+
}
1013+
this->channel->write(R"("snapshot": )", instructions_executed);
1014+
snapshot(m);
1015+
this->channel->write("}\n");
1016+
instructions_executed = 0;
9331017
}
9341018

9351019
void Debugger::freeState(Module *m, uint8_t *interruptData) {
@@ -1511,6 +1595,31 @@ void Debugger::removeOverride(Module *m, uint8_t *interruptData) {
15111595
overrides[fidx.value()].erase(arg);
15121596
}
15131597

1598+
bool Debugger::handleContinueFor(Module *m) {
1599+
if (remaining_instructions < 0) return false;
1600+
1601+
if (remaining_instructions == 0) {
1602+
remaining_instructions = -1;
1603+
if (snapshotPolicy == SnapshotPolicy::checkpointing) {
1604+
checkpoint(m);
1605+
}
1606+
this->channel->write("DONE!\n");
1607+
pauseRuntime(m);
1608+
return true;
1609+
}
1610+
remaining_instructions--;
1611+
return false;
1612+
}
1613+
1614+
void Debugger::notifyCompleteStep(Module *m) const {
1615+
// Upon completing a step in checkpointing mode, make a checkpoint.
1616+
if (m->warduino->debugger->getSnapshotPolicy() ==
1617+
SnapshotPolicy::checkpointing) {
1618+
m->warduino->debugger->checkpoint(m);
1619+
}
1620+
this->channel->write("STEP!\n");
1621+
}
1622+
15141623
Debugger::~Debugger() {
15151624
this->disconnect_proxy();
15161625
this->stop();

β€Žsrc/Debug/debugger.hβ€Ž

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <cstddef>
55
#include <cstdint>
66
#include <mutex>
7+
#include <optional>
78
#include <queue> // std::queue
89
#include <set>
910
#include <thread>
@@ -61,6 +62,7 @@ enum InterruptTypes {
6162
interruptSTEPOver = 0x05,
6263
interruptBPAdd = 0x06,
6364
interruptBPRem = 0x07,
65+
interruptContinueFor = 0x08,
6466
interruptInspect = 0x09,
6567
interruptDUMP = 0x10,
6668
interruptDUMPLocals = 0x11,
@@ -77,7 +79,7 @@ enum InterruptTypes {
7779

7880
// Pull Debugging
7981
interruptSnapshot = 0x60,
80-
interruptEnableSnapshots = 0x61,
82+
interruptSetSnapshotPolicy = 0x61,
8183
interruptLoadSnapshot = 0x62,
8284
interruptMonitorProxies = 0x63,
8385
interruptProxyCall = 0x64,
@@ -100,6 +102,13 @@ enum InterruptTypes {
100102
interruptStored = 0xa1,
101103
};
102104

105+
enum class SnapshotPolicy : int {
106+
none, // Don't automatically take snapshots.
107+
atEveryInstruction, // Take a snapshot after every instruction.
108+
checkpointing, // Take a snapshot every x instructions or at specific
109+
// points where primitives are used.
110+
};
111+
103112
class Debugger {
104113
private:
105114
std::deque<uint8_t *> debugMessages = {};
@@ -119,11 +128,20 @@ class Debugger {
119128
bool connected_to_proxy = false;
120129
warduino::mutex *supervisor_mutex;
121130

122-
bool asyncSnapshots;
123-
131+
// Mocking
124132
std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t>>
125133
overrides;
126134

135+
// Checkpointing
136+
SnapshotPolicy snapshotPolicy;
137+
uint32_t checkpointInterval; // #instructions between checkpoints
138+
uint32_t instructions_executed; // #instructions since last checkpoint
139+
std::optional<uint32_t> fidx_called; // The primitive that was executed
140+
uint32_t prim_args[8]; // The arguments of the executed prim
141+
142+
// Continue for
143+
int32_t remaining_instructions;
144+
127145
// Private methods
128146

129147
void printValue(const StackValue *v, uint32_t idx, bool end) const;
@@ -223,6 +241,9 @@ class Debugger {
223241

224242
void pauseRuntime(const Module *m); // pause runtime for given module
225243

244+
void notifyCompleteStep(
245+
Module *m) const; // notify the debugger frontend that a step was taken
246+
226247
// Interrupts
227248

228249
void addDebugMessage(size_t len, const uint8_t *buff);
@@ -245,9 +266,11 @@ class Debugger {
245266

246267
void snapshot(Module *m) const;
247268

248-
void enableSnapshots(const uint8_t *interruptData);
269+
void setSnapshotPolicy(Module *m, uint8_t *interruptData);
249270

250-
void sendAsyncSnapshots(Module *m) const;
271+
void handleSnapshotPolicy(Module *m);
272+
273+
bool handleContinueFor(Module *m);
251274

252275
void proxify();
253276

@@ -288,4 +311,8 @@ class Debugger {
288311

289312
void addOverride(Module *m, uint8_t *interruptData);
290313
void removeOverride(Module *m, uint8_t *interruptData);
314+
315+
// Checkpointing
316+
void checkpoint(Module *m, bool force = false);
317+
inline SnapshotPolicy getSnapshotPolicy() { return snapshotPolicy; }
291318
};

β€Žsrc/Edward/proxy_supervisor.hβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <cinttypes>
4-
//#include <csignal>
4+
// #include <csignal>
55
#include <mutex>
66
#include <set>
77
#include <thread>

β€Žsrc/Interpreter/interpreter.cppβ€Ž

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ bool Interpreter::interpret(Module *m, bool waiting) {
200200

201201
while ((!program_done && success) || waiting) {
202202
if (m->warduino->program_state == WARDUINOstep) {
203+
m->warduino->debugger->notifyCompleteStep(m);
203204
m->warduino->debugger->pauseRuntime(m);
204205
}
205206

@@ -239,8 +240,14 @@ bool Interpreter::interpret(Module *m, bool waiting) {
239240
}
240241
m->warduino->debugger->skipBreakpoint = nullptr;
241242

243+
if (m->warduino->debugger->handleContinueFor(m)) {
244+
continue;
245+
}
246+
242247
// Take snapshot before executing an instruction
243-
m->warduino->debugger->sendAsyncSnapshots(m);
248+
if (m->warduino->program_state != WARDUINOinit) {
249+
m->warduino->debugger->handleSnapshotPolicy(m);
250+
}
244251

245252
opcode = *m->pc_ptr;
246253
block_ptr = m->pc_ptr;

0 commit comments

Comments
Β (0)