Skip to content

Commit f7c3691

Browse files
committed
enable debug logging per runtime
1 parent c6270f3 commit f7c3691

File tree

6 files changed

+131
-47
lines changed

6 files changed

+131
-47
lines changed

c/interface.c

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,18 @@
5959
#define LOG_LEN 500
6060

6161
#ifdef QTS_DEBUG_MODE
62-
#define QTS_DEBUG(msg) qts_log(msg);
63-
#define QTS_DUMP(value) qts_dump(ctx, value);
62+
#define IF_DEBUG if (QTS_GetContextData(ctx)->debug_log)
63+
#define IF_DEBUG_RT if (QTS_GetRuntimeData(rt)->debug_log)
64+
65+
#define QTS_DEBUG(msg) IF_DEBUG qts_log(msg);
66+
#define QTS_DEBUG_RT(msg) IF_DEBUG_RT qts_log(msg);
67+
#define QTS_DUMP(value) IF_DEBUG qts_dump(ctx, value);
68+
6469
#else
70+
#define IF_DEBUG if (0)
71+
#define IF_DEBUG_RT if (0)
6572
#define QTS_DEBUG(msg) ;
73+
#define QTS_DEBUG_RT(msg) ;
6674
#define QTS_DUMP(value) ;
6775
#endif
6876

@@ -93,6 +101,24 @@
93101
#define IntrinsicsFlags enum QTS_Intrinsic
94102
#define EvalDetectModule int
95103

104+
typedef struct QTS_RuntimeData {
105+
bool debug_log;
106+
} QTS_RuntimeData;
107+
108+
QTS_RuntimeData *QTS_GetRuntimeData(JSRuntime *rt) {
109+
QTS_RuntimeData *data = JS_GetRuntimeOpaque(rt);
110+
if (data == NULL) {
111+
data = malloc(sizeof(QTS_RuntimeData));
112+
data->debug_log = false;
113+
JS_SetRuntimeOpaque(rt, data);
114+
}
115+
return data;
116+
}
117+
118+
QTS_RuntimeData *QTS_GetContextData(JSContext *ctx) {
119+
return QTS_GetRuntimeData(JS_GetRuntime(ctx));
120+
}
121+
96122
void qts_log(char *msg) {
97123
fputs(PKG, stderr);
98124
fputs(msg, stderr);
@@ -273,6 +299,10 @@ JSRuntime *QTS_NewRuntime() {
273299
}
274300

275301
void QTS_FreeRuntime(JSRuntime *rt) {
302+
void *data = JS_GetRuntimeOpaque(rt);
303+
if (data) {
304+
free(data);
305+
}
276306
JS_FreeRuntime(rt);
277307
}
278308

@@ -533,11 +563,11 @@ MaybeAsync(JSValue *) QTS_ExecutePendingJob(JSRuntime *rt, int maxJobsToExecute,
533563
executed++;
534564
}
535565
}
536-
#ifdef QTS_DEBUG_MODE
537-
char msg[500];
538-
snprintf(msg, 500, "QTS_ExecutePendingJob(executed: %d, pctx: %p, lastJobExecuted: %p)", executed, pctx, *lastJobContext);
539-
QTS_DEBUG(msg)
540-
#endif
566+
IF_DEBUG_RT {
567+
char msg[LOG_LEN];
568+
snprintf(msg, LOG_LEN, "QTS_ExecutePendingJob(executed: %d, pctx: %p, lastJobExecuted: %p)", executed, pctx, *lastJobContext);
569+
qts_log(msg);
570+
}
541571
return jsvalue_to_heap(JS_NewFloat64(pctx, executed));
542572
}
543573

@@ -710,10 +740,10 @@ MaybeAsync(JSBorrowedChar *) QTS_Dump(JSContext *ctx, JSValueConst *obj) {
710740
}
711741
}
712742

713-
#ifdef QTS_DEBUG_MODE
714-
qts_log("Error dumping JSON:");
715-
js_std_dump_error(ctx);
716-
#endif
743+
IF_DEBUG {
744+
qts_log("Error dumping JSON:");
745+
js_std_dump_error(ctx);
746+
}
717747

718748
// Fallback: convert to string
719749
return QTS_GetString(ctx, obj);
@@ -731,7 +761,7 @@ JSValue qts_resolve_func_data(
731761

732762
MaybeAsync(JSValue *) QTS_Eval(JSContext *ctx, BorrowedHeapChar *js_code, size_t js_code_length, const char *filename, EvalDetectModule detectModule, EvalFlags evalFlags) {
733763
#ifdef QTS_DEBUG_MODE
734-
char msg[500];
764+
char msg[LOG_LEN];
735765
#endif
736766
if (detectModule) {
737767
if (JS_DetectModule((const char *)js_code, js_code_length)) {
@@ -778,10 +808,10 @@ MaybeAsync(JSValue *) QTS_Eval(JSContext *ctx, BorrowedHeapChar *js_code, size_t
778808
QTS_DEBUG("QTS_Eval: JS_EVAL_TYPE_GLOBAL eval_result")
779809
}
780810

781-
#ifdef QTS_DEBUG_MODE
782-
snprintf(msg, 500, "QTS_Eval: eval_result = %d", eval_result);
783-
QTS_DEBUG(msg)
784-
#endif
811+
IF_DEBUG {
812+
snprintf(msg, LOG_LEN, "QTS_Eval: eval_result = %d", eval_result);
813+
qts_log(msg);
814+
}
785815

786816
if (
787817
// Error - nothing more to do.
@@ -795,10 +825,10 @@ MaybeAsync(JSValue *) QTS_Eval(JSContext *ctx, BorrowedHeapChar *js_code, size_t
795825
// We eval'd a module.
796826
// Make our return type `ModuleExports | Promise<ModuleExports>>`
797827
JSPromiseStateEnum state = JS_PromiseState(ctx, eval_result);
798-
#ifdef QTS_DEBUG_MODE
799-
snprintf(msg, 500, "QTS_Eval: eval_result JS_PromiseState = %i", state);
800-
QTS_DEBUG(msg)
801-
#endif
828+
IF_DEBUG {
829+
snprintf(msg, LOG_LEN, "QTS_Eval: eval_result JS_PromiseState = %i", state);
830+
qts_log(msg);
831+
}
802832
if (
803833
// quickjs@2024-01-14 evaluating module
804834
// produced a promise
@@ -965,6 +995,19 @@ void QTS_TestStringArg(const char *string) {
965995
// pass
966996
}
967997

998+
int QTS_GetDebugLogEnabled(JSRuntime *rt) {
999+
IF_DEBUG_RT {
1000+
return 1;
1001+
}
1002+
return 0;
1003+
}
1004+
1005+
void QTS_SetDebugLogEnabled(JSRuntime *rt, int is_enabled) {
1006+
#ifdef QTS_DEBUG_MODE
1007+
QTS_GetRuntimeData(rt)->debug_log = (bool)is_enabled;
1008+
#endif
1009+
}
1010+
9681011
int QTS_BuildIsDebug() {
9691012
#ifdef QTS_DEBUG_MODE
9701013
return 1;
@@ -1016,11 +1059,11 @@ JSValue qts_call_function(JSContext *ctx, JSValueConst this_val, int argc, JSVal
10161059

10171060
// Function: Host -> QuickJS
10181061
JSValue *QTS_NewFunction(JSContext *ctx, uint32_t func_id, const char *name) {
1019-
#ifdef QTS_DEBUG_MODE
1020-
char msg[500];
1021-
snprintf(msg, 500, "new_function(name: %s, magic: %d)", name, func_id);
1022-
QTS_DEBUG(msg)
1023-
#endif
1062+
IF_DEBUG {
1063+
char msg[LOG_LEN];
1064+
snprintf(msg, LOG_LEN, "new_function(name: %s, magic: %d)", name, func_id);
1065+
qts_log(msg);
1066+
}
10241067
JSValue func_obj = JS_NewCFunctionMagic(
10251068
/* context */ ctx,
10261069
/* JSCFunctionMagic* */ &qts_call_function,
@@ -1101,11 +1144,11 @@ loading, but (1) seems much easier to implement in the sort run.
11011144
*/
11021145

11031146
JSModuleDef *qts_compile_module(JSContext *ctx, const char *module_name, BorrowedHeapChar *module_body) {
1104-
#ifdef QTS_DEBUG_MODE
1105-
char msg[500];
1106-
sprintf(msg, "QTS_CompileModule(ctx: %p, name: %s, bodyLength: %lu)", ctx, module_name, strlen(module_body));
1107-
QTS_DEBUG(msg)
1108-
#endif
1147+
IF_DEBUG {
1148+
char msg[LOG_LEN];
1149+
sprintf(msg, "QTS_CompileModule(ctx: %p, name: %s, bodyLength: %lu)", ctx, module_name, strlen(module_body));
1150+
qts_log(msg);
1151+
}
11091152
JSValue func_val = JS_Eval(ctx, module_body, strlen(module_body), module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
11101153
if (JS_IsException(func_val)) {
11111154
return NULL;
@@ -1146,11 +1189,11 @@ EM_JS(MaybeAsync(char *), qts_host_normalize_module, (JSRuntime * rt, JSContext
11461189
// See js_module_loader in quickjs/quickjs-libc.c:567
11471190
JSModuleDef *qts_load_module(JSContext *ctx, const char *module_name, void *_unused) {
11481191
JSRuntime *rt = JS_GetRuntime(ctx);
1149-
#ifdef QTS_DEBUG_MODE
1150-
char msg[500];
1151-
sprintf(msg, "qts_load_module(rt: %p, ctx: %p, name: %s)", rt, ctx, module_name);
1152-
QTS_DEBUG(msg)
1153-
#endif
1192+
IF_DEBUG_RT {
1193+
char msg[LOG_LEN];
1194+
sprintf(msg, "qts_load_module(rt: %p, ctx: %p, name: %s)", rt, ctx, module_name);
1195+
qts_log(msg);
1196+
}
11541197
char *module_source = qts_host_load_module_source(rt, ctx, module_name);
11551198
if (module_source == NULL) {
11561199
return NULL;
@@ -1163,11 +1206,11 @@ JSModuleDef *qts_load_module(JSContext *ctx, const char *module_name, void *_unu
11631206

11641207
char *qts_normalize_module(JSContext *ctx, const char *module_base_name, const char *module_name, void *_unused) {
11651208
JSRuntime *rt = JS_GetRuntime(ctx);
1166-
#ifdef QTS_DEBUG_MODE
1167-
char msg[500];
1168-
sprintf(msg, "qts_normalize_module(rt: %p, ctx: %p, base_name: %s, name: %s)", rt, ctx, module_base_name, module_name);
1169-
QTS_DEBUG(msg)
1170-
#endif
1209+
IF_DEBUG_RT {
1210+
char msg[LOG_LEN];
1211+
sprintf(msg, "qts_normalize_module(rt: %p, ctx: %p, base_name: %s, name: %s)", rt, ctx, module_base_name, module_name);
1212+
qts_log(msg);
1213+
}
11711214
char *em_module_name = qts_host_normalize_module(rt, ctx, module_base_name, module_name);
11721215
char *js_module_name = js_strdup(ctx, em_module_name);
11731216
free(em_module_name);

packages/quickjs-emscripten-core/src/context-asyncify.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type {
88
} from "@jitl/quickjs-ffi-types"
99
import type { ContextResult } from "./context"
1010
import { QuickJSContext } from "./context"
11-
import { debugLog } from "./debug"
1211
import type { Lifetime } from "./lifetime"
1312
import type { QuickJSModuleCallbacks } from "./module"
1413
import type { QuickJSAsyncRuntime } from "./runtime-asyncify"
@@ -65,7 +64,7 @@ export class QuickJSAsyncContext extends QuickJSContext {
6564
),
6665
)
6766
} catch (error) {
68-
debugLog("QTS_Eval_MaybeAsync threw", error)
67+
this.runtime.debugLog("QTS_Eval_MaybeAsync threw", error)
6968
throw error
7069
}
7170
const errorPtr = this.ffi.QTS_ResolveException(this.ctx.value, resultPtr)

packages/quickjs-emscripten-core/src/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,7 @@ export class QuickJSContext
13661366
const result = yield* awaited(fn.apply(thisHandle, argHandles))
13671367
if (result) {
13681368
if ("error" in result && result.error) {
1369-
debugLog("throw error", result.error)
1369+
this.runtime.debugLog("throw error", result.error)
13701370
throw result.error
13711371
}
13721372
const handle = scope.manage(result instanceof Lifetime ? result : result.value)

packages/quickjs-emscripten-core/src/runtime.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,40 @@ export class QuickJSRuntime extends UsingDisposable implements Disposable {
328328
}
329329
}
330330

331+
private _debugMode = false
332+
333+
/**
334+
* Enable or disable debug logging.
335+
*
336+
* If this module is a DEBUG variant, more logs will be printed from the C
337+
* code.
338+
*/
339+
setDebugMode(enabled: boolean) {
340+
this._debugMode = enabled
341+
if (this.ffi.DEBUG && this.rt.alive) {
342+
this.ffi.QTS_SetDebugLogEnabled(this.rt.value, enabled ? 1 : 0)
343+
}
344+
}
345+
346+
/**
347+
* @returns true if debug logging is enabled
348+
*/
349+
isDebugMode(): boolean {
350+
return this._debugMode
351+
}
352+
353+
/**
354+
* In debug mode, log the result of calling `msg()`.
355+
*
356+
* We take a function instead of a log message to avoid expensive string
357+
* manipulation if debug logging is disabled.
358+
*/
359+
debugLog(...msg: unknown[]) {
360+
if (this._debugMode) {
361+
console.log("QuickJS:", ...msg)
362+
}
363+
}
364+
331365
private getSystemContext() {
332366
if (!this.context) {
333367
// We own this context and should dispose of it.
@@ -370,7 +404,7 @@ export class QuickJSRuntime extends UsingDisposable implements Disposable {
370404
const result = yield* awaited(moduleLoader(moduleName, context))
371405

372406
if (typeof result === "object" && "error" in result && result.error) {
373-
debugLog("cToHostLoadModule: loader returned error", result.error)
407+
this.debugLog("cToHostLoadModule: loader returned error", result.error)
374408
throw result.error
375409
}
376410

@@ -379,7 +413,7 @@ export class QuickJSRuntime extends UsingDisposable implements Disposable {
379413

380414
return this.memory.newHeapCharPointer(moduleSource).value.ptr
381415
} catch (error) {
382-
debugLog("cToHostLoadModule: caught error", error)
416+
this.debugLog("cToHostLoadModule: caught error", error)
383417
context.throw(error as any)
384418
return 0 as BorrowedHeapCharPointer
385419
}
@@ -410,14 +444,14 @@ export class QuickJSRuntime extends UsingDisposable implements Disposable {
410444
)
411445

412446
if (typeof result === "object" && "error" in result && result.error) {
413-
debugLog("cToHostNormalizeModule: normalizer returned error", result.error)
447+
this.debugLog("cToHostNormalizeModule: normalizer returned error", result.error)
414448
throw result.error
415449
}
416450

417451
const name = typeof result === "string" ? result : result.value
418452
return context.getMemory(this.rt.value).newHeapCharPointer(name).value.ptr
419453
} catch (error) {
420-
debugLog("normalizeModule: caught error", error)
454+
this.debugLog("normalizeModule: caught error", error)
421455
context.throw(error as any)
422456
return 0 as BorrowedHeapCharPointer
423457
}

packages/quickjs-ffi-types/src/ffi-async.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ export interface QuickJSAsyncFFI {
5353
QTS_NewRuntime: () => JSRuntimePointer
5454
QTS_FreeRuntime: (rt: JSRuntimePointer) => void
5555
QTS_NewContext: (rt: JSRuntimePointer, intrinsics: IntrinsicsFlags) => JSContextPointer
56+
QTS_GetContextData: (ctx: JSContextPointer) => QTS_RuntimeDataPointer
57+
QTS_GetRuntimeData: (rt: JSRuntimePointer) => QTS_RuntimeDataPointer
5658
QTS_FreeContext: (ctx: JSContextPointer) => void
5759
QTS_FreeValuePointer: (ctx: JSContextPointer, value: JSValuePointer) => void
5860
QTS_FreeValuePointerRuntime: (rt: JSRuntimePointer, value: JSValuePointer) => void
@@ -242,6 +244,8 @@ export interface QuickJSAsyncFFI {
242244
promise: JSValuePointer | JSValueConstPointer,
243245
) => JSValuePointer
244246
QTS_TestStringArg: (string: string) => void
247+
QTS_GetDebugLogEnabled: (rt: JSRuntimePointer) => number
248+
QTS_SetDebugLogEnabled: (rt: JSRuntimePointer, is_enabled: number) => void
245249
QTS_BuildIsDebug: () => number
246250
QTS_BuildIsAsyncify: () => number
247251
QTS_NewFunction: (ctx: JSContextPointer, func_id: number, name: string) => JSValuePointer

packages/quickjs-ffi-types/src/ffi.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export interface QuickJSFFI {
5252
QTS_NewRuntime: () => JSRuntimePointer
5353
QTS_FreeRuntime: (rt: JSRuntimePointer) => void
5454
QTS_NewContext: (rt: JSRuntimePointer, intrinsics: IntrinsicsFlags) => JSContextPointer
55+
QTS_GetContextData: (ctx: JSContextPointer) => QTS_RuntimeDataPointer
56+
QTS_GetRuntimeData: (rt: JSRuntimePointer) => QTS_RuntimeDataPointer
5557
QTS_FreeContext: (ctx: JSContextPointer) => void
5658
QTS_FreeValuePointer: (ctx: JSContextPointer, value: JSValuePointer) => void
5759
QTS_FreeValuePointerRuntime: (rt: JSRuntimePointer, value: JSValuePointer) => void
@@ -190,6 +192,8 @@ export interface QuickJSFFI {
190192
promise: JSValuePointer | JSValueConstPointer,
191193
) => JSValuePointer
192194
QTS_TestStringArg: (string: string) => void
195+
QTS_GetDebugLogEnabled: (rt: JSRuntimePointer) => number
196+
QTS_SetDebugLogEnabled: (rt: JSRuntimePointer, is_enabled: number) => void
193197
QTS_BuildIsDebug: () => number
194198
QTS_BuildIsAsyncify: () => number
195199
QTS_NewFunction: (ctx: JSContextPointer, func_id: number, name: string) => JSValuePointer

0 commit comments

Comments
 (0)