135 lines
4.3 KiB
C
135 lines
4.3 KiB
C
// testTrace.c -- cross-boundary stack traces. A Lua context exports a function that errors; a
|
|
// JavaScript context calls it by name. The failure crosses the context/engine boundary, and the
|
|
// error delivered to the handler carries the boundary tag ("[lua ctx N] ...") -- so a cross-engine
|
|
// failure is readable without changing the error handler's signature.
|
|
|
|
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include "calog.h"
|
|
#include "calogExport.h"
|
|
|
|
#include <stdatomic.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#define PUMP_LIMIT 4000
|
|
|
|
static CalogT *calog = NULL;
|
|
static _Atomic bool ready = false;
|
|
static _Atomic int32_t errorCount = 0;
|
|
static char lastMessage[512] = { 0 };
|
|
static int32_t testsRun = 0;
|
|
static int32_t testsFailed = 0;
|
|
|
|
static _Atomic bool reported = false;
|
|
|
|
static void checkImpl(bool condition, const char *message, int32_t line);
|
|
static int32_t nativeReady(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData);
|
|
static int32_t nativeReport(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData);
|
|
static void onError(uint64_t contextId, const char *message, void *userData);
|
|
static void pumpUntil(_Atomic bool *flag);
|
|
|
|
#define CHECK(cond, msg) checkImpl((cond), (msg), __LINE__)
|
|
|
|
|
|
static void checkImpl(bool condition, const char *message, int32_t line) {
|
|
testsRun++;
|
|
if (!condition) {
|
|
testsFailed++;
|
|
printf("FAIL testTrace.c:%d %s\n", line, message);
|
|
}
|
|
}
|
|
|
|
|
|
static int32_t nativeReady(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) {
|
|
(void)args;
|
|
(void)argCount;
|
|
(void)userData;
|
|
atomic_store(&ready, true);
|
|
calogValueNil(result);
|
|
return calogOkE;
|
|
}
|
|
|
|
|
|
static int32_t nativeReport(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) {
|
|
(void)userData;
|
|
calogValueNil(result);
|
|
if (argCount == 1 && args[0].type == calogStringE) {
|
|
size_t len;
|
|
len = (size_t)args[0].as.s.length;
|
|
if (len >= sizeof(lastMessage)) {
|
|
len = sizeof(lastMessage) - 1;
|
|
}
|
|
memcpy(lastMessage, args[0].as.s.bytes, len);
|
|
lastMessage[len] = '\0';
|
|
atomic_store(&reported, true);
|
|
}
|
|
return calogOkE;
|
|
}
|
|
|
|
|
|
static void onError(uint64_t contextId, const char *message, void *userData) {
|
|
(void)contextId;
|
|
(void)message;
|
|
(void)userData;
|
|
atomic_fetch_add(&errorCount, 1);
|
|
}
|
|
|
|
|
|
static void pumpUntil(_Atomic bool *flag) {
|
|
struct timespec ts = { 0, 500000 };
|
|
int32_t i;
|
|
|
|
for (i = 0; i < PUMP_LIMIT; i++) {
|
|
calogPump(calog);
|
|
if (atomic_load(flag)) {
|
|
calogPump(calog);
|
|
return;
|
|
}
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
CalogContextT *lua;
|
|
CalogContextT *js;
|
|
_Atomic bool errored = false;
|
|
int32_t before;
|
|
|
|
calog = calogCreate();
|
|
if (calog == NULL) {
|
|
printf("calog create failed\n");
|
|
return 1;
|
|
}
|
|
calogSetErrorHandler(calog, onError, NULL);
|
|
calogExportRegister(calog);
|
|
calogRegister(calog, "ready", nativeReady, NULL);
|
|
calogRegister(calog, "report", nativeReport, NULL);
|
|
|
|
// A Lua context exports a function that raises.
|
|
lua = calogContextOpen(calog, &calogLuaEngine);
|
|
calogContextEval(lua, "calogExport('boom', function() error('kaboom in lua') end); ready()");
|
|
pumpUntil(&ready);
|
|
CHECK(atomic_load(&ready), "lua context published the export");
|
|
|
|
// A JavaScript context calls the Lua export by name and catches the failure; the caught error
|
|
// is the cross-boundary-tagged message.
|
|
before = atomic_load(&errorCount);
|
|
js = calogContextOpen(calog, &calogJsEngine);
|
|
calogContextEval(js, "try { calogCall('boom'); } catch (e) { report(String(e)); }");
|
|
pumpUntil(&reported);
|
|
(void)errored;
|
|
(void)before;
|
|
CHECK(atomic_load(&reported), "the cross-engine call failed and JS caught it");
|
|
CHECK(strstr(lastMessage, "kaboom in lua") != NULL, "the original engine error survived the crossing");
|
|
CHECK(strstr(lastMessage, "lua ctx") != NULL, "the error is tagged with the boundary it unwound through");
|
|
|
|
calogDestroy(calog);
|
|
|
|
printf("last message: %s\n", lastMessage);
|
|
printf("\n%d checks, %d failed\n", testsRun, testsFailed);
|
|
fflush(stdout);
|
|
return testsFailed == 0 ? 0 : 1;
|
|
}
|