Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
104 changes: 102 additions & 2 deletions src/Debug/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <algorithm>
#include <cinttypes>
#include <cstring>
#include <optional>
#ifndef ARDUINO
#include <nlohmann/json.hpp>
#else
Expand Down Expand Up @@ -332,6 +333,14 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) {
this->dumpCallbackmapping();
free(interruptData);
break;
case interruptSetOverridePinValue:
this->addOverride(m, interruptData + 1);
free(interruptData);
break;
case interruptUnsetOverridePinValue:
this->removeOverride(m, interruptData + 1);
free(interruptData);
break;
default:
// handle later
this->channel->write("COULD not parse interrupt data!\n");
Expand Down Expand Up @@ -725,7 +734,7 @@ bool Debugger::handlePushedEvent(char *bytes) const {
}

void Debugger::snapshot(Module *m) const {
uint16_t numberBytes = 11;
uint16_t numberBytes = 12;
uint8_t state[] = {pcState,
breakpointsState,
callstackState,
Expand All @@ -736,7 +745,8 @@ void Debugger::snapshot(Module *m) const {
stackState,
callbacksState,
eventsState,
ioState};
ioState,
overridesState};
inspect(m, numberBytes, state);
}

Expand Down Expand Up @@ -885,6 +895,23 @@ void Debugger::inspect(Module *m, const uint16_t sizeStateArray,
addComma = true;
break;
}
case overridesState: {
this->channel->write("%s", addComma ? "," : "");
this->channel->write(R"("overrides": [)");
bool comma = false;
for (auto key : overrides) {
for (auto argResult : key.second) {
this->channel->write("%s", comma ? ", " : "");
this->channel->write(
R"({"fidx": %d, "arg": %d, "return_value": %d})",
key.first, argResult.first, argResult.second);
comma = true;
}
}
this->channel->write("]");
addComma = true;
break;
}
default: {
debug("dumpExecutionState: Received unknown state request\n");
break;
Expand Down Expand Up @@ -1240,6 +1267,19 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) {
restore_external_state(m, external_state);
break;
}
case overridesState: {
debug("receiving overridesState\n");
overrides.clear();
uint8_t overrides_count = *program_state++;
for (uint32_t i = 0; i < overrides_count; i++) {
uint32_t fidx = read_B32(&program_state);
uint32_t arg = read_B32(&program_state);
uint32_t return_value = read_B32(&program_state);
overrides[fidx][arg] = return_value;
debug("Override %d %d %d\n", fidx, arg, return_value);
}
break;
}
default: {
FATAL("saveState: Received unknown program state\n");
}
Expand Down Expand Up @@ -1411,6 +1451,66 @@ bool Debugger::reset(Module *m) const {
return true;
}

std::optional<uint32_t> resolve_imported_function(Module *m,
std::string function_name) {
for (uint32_t fidx = 0; fidx < m->import_count; fidx++) {
if (!strcmp(m->functions[fidx].import_field, function_name.c_str())) {
return fidx;
}
}
return {};
}

std::string read_string(uint8_t **pos) {
std::string str = "";
char c = *(*pos)++;
while (c != '\0') {
str += c;
c = *(*pos)++;
}
return str;
}

void Debugger::addOverride(Module *m, uint8_t *interruptData) {
std::string primitive_name = read_string(&interruptData);
uint32_t arg = read_B32(&interruptData);
uint32_t result = read_B32(&interruptData);

std::optional<uint32_t> fidx = resolve_imported_function(m, primitive_name);
if (!fidx) {
channel->write(
"Cannot override the result for unknown function \"%s\".\n",
primitive_name.c_str());
return;
}

channel->write("Override %s(%d) = %d.\n", primitive_name.c_str(), arg,
result);
overrides[fidx.value()][arg] = result;
}

void Debugger::removeOverride(Module *m, uint8_t *interruptData) {
std::string primitive_name = read_string(&interruptData);
uint32_t arg = read_B32(&interruptData);

std::optional<uint32_t> fidx = resolve_imported_function(m, primitive_name);
if (!fidx) {
channel->write("Cannot remove override for unknown function \"%s\".\n",
primitive_name.c_str());
return;
}

if (overrides[fidx.value()].count(arg) == 0) {
channel->write("Override for %s(%d) not found.\n",
primitive_name.c_str(), arg);
return;
}

channel->write("Removing override %s(%d) = %d.\n", primitive_name.c_str(),
arg, overrides[fidx.value()][arg]);
overrides[fidx.value()].erase(arg);
}

Debugger::~Debugger() {
this->disconnect_proxy();
this->stop();
Expand Down
20 changes: 19 additions & 1 deletion src/Debug/debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ enum ExecutionState {
callbacksState = 0x09,
eventsState = 0x0A,
ioState = 0x0B,
overridesState = 0x0C,
};

enum InterruptTypes {
Expand Down Expand Up @@ -90,10 +91,13 @@ enum InterruptTypes {
interruptDUMPCallbackmapping = 0x74,
interruptRecvCallbackmapping = 0x75,

// Primitive overrides
interruptSetOverridePinValue = 0x80,
interruptUnsetOverridePinValue = 0x81,

// Operations
interruptStore = 0xa0,
interruptStored = 0xa1,

};

class Debugger {
Expand All @@ -117,6 +121,9 @@ class Debugger {

bool asyncSnapshots;

std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t>>
overrides;

// Private methods

void printValue(const StackValue *v, uint32_t idx, bool end) const;
Expand Down Expand Up @@ -270,4 +277,15 @@ class Debugger {
void notifyPushedEvent() const;

bool handlePushedEvent(char *bytes) const;

// Concolic Multiverse Debugging
inline bool isMocked(uint32_t fidx, uint32_t argument) {
return overrides.count(fidx) > 0 && overrides[fidx].count(argument) > 0;
}
inline uint32_t getMockedValue(uint32_t fidx, uint32_t argument) {
return overrides[fidx][argument];
}

void addOverride(Module *m, uint8_t *interruptData);
void removeOverride(Module *m, uint8_t *interruptData);
};
10 changes: 10 additions & 0 deletions src/Interpreter/instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ bool i_instr_call(Module *m) {
}

if (fidx < m->import_count) {
// Mocking only works on primitives, no need to check for it otherwise.
if (m->sp >= 0) {
uint32_t arg = m->stack[m->sp].value.uint32;
if (m->warduino->debugger->isMocked(fidx, arg)) {
m->stack[m->sp].value.uint32 =
m->warduino->debugger->getMockedValue(fidx, arg);
return true;
}
}

return ((Primitive)m->functions[fidx].func_ptr)(m);
} else {
if (m->csp >= CALLSTACK_SIZE) {
Expand Down