Skip to content

Commit 65b06ef

Browse files
committed
set up tracing for SQLite execution (sqlite3_trace_v2):
* create 'libtrace.c' shim (for registering of trace callbacks) * add JS-side shims for tracing * TODO: only SQLITE_TRACE_STMT is currently supported: support other trace messages
1 parent a3b1324 commit 65b06ef

File tree

8 files changed

+108
-13
lines changed

8 files changed

+108
-13
lines changed

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ EXTENSION_FUNCTIONS_URL = https://www.sqlite.org/contrib/download/extension-func
77
EXTENSION_FUNCTIONS_SHA3 = ee39ddf5eaa21e1d0ebcbceeab42822dd0c4f82d8039ce173fd4814807faabfa
88

99
# source files
10+
# TODO: maybe use libtrace only in debug mode
1011
CFILES = \
1112
sqlite3.c \
1213
extension-functions.c \
@@ -16,14 +17,16 @@ CFILES = \
1617
libhook.c \
1718
libprogress.c \
1819
libvfs.c \
20+
libtrace.c \
1921
$(CFILES_EXTRA)
2022

2123
JSFILES = \
2224
src/libauthorizer.js \
2325
src/libfunction.js \
2426
src/libhook.js \
2527
src/libprogress.js \
26-
src/libvfs.js
28+
src/libvfs.js \
29+
src/libtrace.js
2730

2831
vpath %.c src
2932
vpath %.c deps
@@ -77,7 +80,8 @@ EMFLAGS_LIBRARIES = \
7780
--post-js src/libfunction.js \
7881
--post-js src/libhook.js \
7982
--post-js src/libprogress.js \
80-
--post-js src/libvfs.js
83+
--post-js src/libvfs.js \
84+
--post-js src/libtrace.js
8185

8286
EMFLAGS_ASYNCIFY_COMMON = \
8387
-s ASYNCIFY \

src/libadapters.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ DECLARE(void, vppp, P, P, P);
2222
DECLARE(I, ipppj, P, P, P, J);
2323
DECLARE(I, ipppi, P, P, P, I);
2424
DECLARE(I, ipppp, P, P, P, P);
25+
DECLARE(I, ippipp, P, P, I, P, P);
2526
DECLARE(I, ipppip, P, P, P, I, P);
2627
DECLARE(void, vpppip, P, P, P, I, P);
2728
DECLARE(I, ippppi, P, P, P, P, I);

src/libadapters.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const SIGNATURES = [
66
'ipppj', // xTruncate
77
'ipppi', // xSleep, xSync, xLock, xUnlock, xShmUnmap
88
'ipppp', // xFileSize, xCheckReservedLock, xCurrentTime, xCurrentTimeInt64
9+
'ippipp', // xTrace
910
'ipppip', // xFileControl, xRandomness, xGetLastError
1011
'vpppip', // xFunc, xStep
1112
'ippppi', // xDelete

src/libtrace.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <emscripten.h>
2+
#include <sqlite3.h>
3+
#include <stdio.h>
4+
5+
#include "libadapters.h"
6+
7+
#define CALL_JS(SIGNATURE, KEY, ...) \
8+
(asyncFlags ? SIGNATURE##_async(KEY, __VA_ARGS__) \
9+
: SIGNATURE(KEY, __VA_ARGS__))
10+
11+
static int libtrace_xTrace(unsigned opCode, void *pApp, void *P, void *X) {
12+
const int asyncFlags = pApp ? *(int *)pApp : 0;
13+
return CALL_JS(ippipp, pApp, pApp, opCode, P, X);
14+
}
15+
16+
void EMSCRIPTEN_KEEPALIVE libtrace_trace(sqlite3 *db, unsigned mTrace,
17+
int xTrace, void *pApp) {
18+
sqlite3_trace_v2(db, mTrace, xTrace ? &libtrace_xTrace : NULL, pApp);
19+
}

src/libtrace.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
2+
// This file should be included in the build with --post-js.
3+
(function() {
4+
const AsyncFunction = Object.getPrototypeOf(async function() { }).constructor;
5+
let pAsyncFlags = 0;
6+
7+
Module['trace'] = function(db, mTrace, xTrace) {
8+
if (pAsyncFlags) {
9+
Module['deleteCallback'](pAsyncFlags);
10+
Module['_sqlite3_free'](pAsyncFlags);
11+
pAsyncFlags = 0;
12+
}
13+
14+
pAsyncFlags = Module['_sqlite3_malloc'](4);
15+
setValue(pAsyncFlags, xTrace instanceof AsyncFunction ? 1 : 0, 'i32');
16+
17+
ccall(
18+
'libtrace_trace',
19+
'void',
20+
['number', 'number', 'number', 'number'],
21+
[db, mTrace, xTrace ? 1 : 0, pAsyncFlags]);
22+
if (xTrace) {
23+
Module['setCallback'](pAsyncFlags, (_, opCode, pP, pX) => {
24+
return xTrace(opCode, pP, pX)
25+
});
26+
}
27+
};
28+
})();

src/sqlite-api.js

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export * from './sqlite-constants.js';
66
const MAX_INT64 = 0x7fffffffffffffffn;
77
const MIN_INT64 = -0x8000000000000000n;
88

9-
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
9+
const AsyncFunction = Object.getPrototypeOf(async function() { }).constructor;
1010

1111
export class SQLiteError extends Error {
1212
constructor(message, code) {
@@ -248,7 +248,7 @@ export function Factory(Module) {
248248
return check(fname, result, mapStmtToDB.get(stmt));
249249
};
250250
})();
251-
251+
252252
sqlite3.close = (function() {
253253
const fname = 'sqlite3_close';
254254
const f = Module.cwrap(fname, ...decl('n:n'), { async });
@@ -388,7 +388,7 @@ export function Factory(Module) {
388388

389389
sqlite3.create_function = function(db, zFunctionName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) {
390390
verifyDatabase(db);
391-
391+
392392
// Convert SQLite callback arguments to JavaScript-friendly arguments.
393393
function adapt(f) {
394394
return f instanceof AsyncFunction ?
@@ -640,7 +640,7 @@ export function Factory(Module) {
640640
const result = Module.set_authorizer(db, adapt(xAuth), pApp);
641641
return check('sqlite3_set_authorizer', result, db);
642642
};;
643-
643+
644644
sqlite3.sql = (function() {
645645
const fname = 'sqlite3_sql';
646646
const f = Module.cwrap(fname, ...decl('n:s'));
@@ -673,7 +673,7 @@ export function Factory(Module) {
673673
onFinally.push(() => Module._sqlite3_free(pzHead));
674674
Module.HEAPU8.set(utf8, pzHead);
675675
Module.HEAPU8[pzEnd - 1] = 0;
676-
676+
677677
// Use extra space for the statement handle and SQL tail pointer.
678678
const pStmt = pzHead + allocSize - 8;
679679
const pzTail = pzHead + allocSize - 4;
@@ -687,7 +687,7 @@ export function Factory(Module) {
687687
stmt = 0;
688688
}
689689
onFinally.push(maybeFinalize);
690-
690+
691691
// Loop over statements.
692692
Module.setValue(pzTail, pzHead, '*');
693693
do {
@@ -710,7 +710,7 @@ export function Factory(Module) {
710710
if (rc !== SQLite.SQLITE_OK) {
711711
check('sqlite3_prepare_v3', rc, db);
712712
}
713-
713+
714714
stmt = Module.getValue(pStmt, '*');
715715
if (stmt) {
716716
mapStmtToDB.set(stmt, db);
@@ -752,7 +752,7 @@ export function Factory(Module) {
752752
iUpdateType,
753753
Module.UTF8ToString(dbName),
754754
Module.UTF8ToString(tblName),
755-
cvt32x2ToBigInt(lo32, hi32)
755+
cvt32x2ToBigInt(lo32, hi32)
756756
];
757757
};
758758
function adapt(f) {
@@ -762,7 +762,39 @@ export function Factory(Module) {
762762
}
763763

764764
Module.update_hook(db, adapt(xUpdateHook));
765-
};;
765+
};
766+
767+
sqlite3.trace = function(db, mTrace, xTrace) {
768+
verifyDatabase(db)
769+
770+
function cvtArgs(opCode, _pP, pX) {
771+
// NOTE: only SQLITE_TRACE_STMT is currently implemented
772+
switch (opCode) {
773+
case SQLite.SQLITE_TRACE_STMT:
774+
return [
775+
opCode,
776+
"SQLITE_TRACE_STMT",
777+
Module.UTF8ToString(pX)
778+
]
779+
default:
780+
// TODO: implement other variants
781+
return [
782+
opCode,
783+
"UNSUPPORTED_OP",
784+
null
785+
]
786+
}
787+
}
788+
789+
function adapt(f) {
790+
return f instanceof AsyncFunction ?
791+
(async (opCode, pP, pX) => f(...cvtArgs(opCode, pP, pX))) :
792+
((opCode, pP, pX) => f(...cvtArgs(opCode, pP, pX)))
793+
}
794+
795+
Module.trace(db, mTrace, adapt(xTrace))
796+
}
797+
766798

767799
sqlite3.value = function(pValue) {
768800
const type = sqlite3.value_type(pValue);
@@ -876,7 +908,7 @@ export function Factory(Module) {
876908
await Promise.all(Module.retryOps);
877909
Module.retryOps = [];
878910
}
879-
911+
880912
rc = await f();
881913

882914
// Retry on failure with new pending retry operations.

src/sqlite-constants.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,4 +272,9 @@ export const SQLITE_LIMIT_WORKER_THREADS = 11;
272272

273273
export const SQLITE_PREPARE_PERSISTENT = 0x01;
274274
export const SQLITE_PREPARE_NORMALIZED = 0x02;
275-
export const SQLITE_PREPARE_NO_VTAB = 0x04;
275+
export const SQLITE_PREPARE_NO_VTAB = 0x04;
276+
277+
export const SQLITE_TRACE_STMT = 0x01;
278+
export const SQLITE_TRACE_PROFILE = 0x02;
279+
export const SQLITE_TRACE_ROW = 0x04;
280+
export const SQLITE_TRACE_CLOSE = 0x08;

src/types/index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,11 @@ declare interface SQLiteAPI {
849849
* @returns `SQLITE_OK` (throws exception on error)
850850
*/
851851
vfs_register(vfs: SQLiteVFS, makeDefault?: boolean): number;
852+
853+
trace(
854+
db: number, mTrace: 1 | 2 | 3 | 4,
855+
xTrace: (opCode: 1 | 2 | 3 | 4, opStr: string, sql?: string) => number
856+
): void
852857
}
853858

854859
/** @ignore */

0 commit comments

Comments
 (0)