Skip to content

Commit

Permalink
Merge pull request bellard#8 from hsiaosiyuan0/feat/gc-dump
Browse files Browse the repository at this point in the history
feat/gc-dump
  • Loading branch information
hsiaosiyuan0 authored Mar 18, 2023
2 parents 16b9f09 + bff8311 commit 4b8cc27
Show file tree
Hide file tree
Showing 31 changed files with 1,640 additions and 90 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/build/qjs/qjs",
"args": ["--debug", "8097"],
// "args": ["tmp_test_1.mjs"],
// "args": ["--debug", "8097"],
"args": ["tmp_test.js"],
"cwd": "${workspaceFolder}"
}
]
Expand Down
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"DATAVIEW",
"dbuf",
"DCMAKE",
"dctx",
"DECOMP",
"DECREF",
"divrem",
Expand All @@ -64,17 +65,20 @@
"FPROUND",
"fsetter",
"ftoa",
"gcdump",
"getset",
"gosub",
"hasexpr",
"hasval",
"heapsnapshot",
"HTMLDDA",
"indexof",
"INTRINS",
"isqrt",
"jscalc",
"JSGC",
"jsstring",
"JSVALUE",
"libbf",
"LIBREGEXP",
"LIBUNICODE",
Expand All @@ -86,6 +90,7 @@
"MAPSET",
"Microbench",
"MKPTR",
"MKVAL",
"nfinity",
"NFKC",
"NFKD",
Expand Down Expand Up @@ -156,6 +161,7 @@
"TYPARR",
"TYPEDARR",
"typedarray",
"uctx",
"uncatchable",
"unconsistent",
"unparenthesized",
Expand Down
62 changes: 59 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ SlowJS - QuickJS is quick but I can make it slow!

Learning the awesome [QuickJS](https://github.com/bellard/quickjs) by extending it with below functionalities:

- [x] Divide the 5.4W LoC [quickjs.c](https://github.com/bellard/quickjs/blob/master/quickjs.c) into multiple small files, which makes the code easy to browser and navigate
- [x] Divide the 5.4W LoC [quickjs.c](https://github.com/bellard/quickjs) into multiple small files, makes the code easy to browser and navigate
- [x] A debugger which supports inline breakpoints and includes web interfaces which is easy to integrate with the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
- [ ] Dump the GC managed objects and view the results in the Chrome inspector
- [x] Dump the GC managed objects and view the results in the Chrome devtools

## Debugger

The debugger can be tasted by following steps:

<details>
<summary>Click to expand</summary>

1. Build our SlowJS:

```bash
Expand Down Expand Up @@ -167,11 +170,64 @@ the output looks like:
11. Now the test script is done and the debugger server prints the final results:

```bash
client closed, stopping sess thread...
new sess thread is running...
2
```

</details>

## GC Dump

The GC dump functionality can be tasted by following steps:

<details>
<summary>Click to expand</summary>


1. Build our SlowJS:

```bash
cmake -S . --preset=default
cmake --build --preset=qjs
```

the location of the built stuff is `./build/qjs/qjs`

2. Make up a file `tmp_test.js` to test:

```js
var o = {
a: { a1: { a2: 1 } },
b: { b1: { b2: 1 } },
c: function () {
return 1;
},
d: new ArrayBuffer((1 << 20) * 50, 0),
e: new Uint16Array((1 << 20) * 50, 0),
};

__js_gcdump_objects();
print(o); // retain the obj to prevent it from being freed
```

3. Run the test script:

```bash
./build/qjs/qjs tmp_test.js
```

4. The output file will have name looks like `Heap.20230318.123156.775845.heapsnapshot` in this pattern `Heap.date.time.ms.heapsnapshot`

5. Import the output file into Chrome devtools:

![](/docs/imgs/chrome-devtools-load-heap.png)

6. The we can dig into the heap:

![](/docs/imgs/chrome-devtools-heap.png)

</details>

## Development

It's better to glance over the available options before you perform the actual build:
Expand Down
Binary file added docs/imgs/chrome-devtools-heap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/imgs/chrome-devtools-load-heap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions include/quickjs-libc.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
JSValue js_debug_pc2line(JSContext *ctx, JSValueConst this_val, int argc,
JSValueConst *argv);

JSValue js_gcdump_objects(JSContext *ctx, JSValueConst this_val, int argc,
JSValueConst *argv);

#ifdef __cplusplus
} /* extern "C" { */
#endif
Expand Down
70 changes: 70 additions & 0 deletions include/quickjs.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,77 @@ void JS_FreeRuntime(JSRuntime *rt);
void *JS_GetRuntimeOpaque(JSRuntime *rt);
void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque);
typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp);
typedef struct JSProperty JSProperty;
typedef struct JSShapeProperty JSShapeProperty;
void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
void JS_RunGC(JSRuntime *rt);
JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj);

// node types come from the v8's impl
enum {
JSGCDumpEdge_TYPE_CTX_VAR, // A variable from a function context.
JSGCDumpEdge_TYPE_ELEM, // An element of an array.
JSGCDumpEdge_TYPE_PROP, // A named object property.
JSGCDumpEdge_TYPE_INTERNAL, // A link that can't be accessed from JS,
// thus, its name isn't a real property name
// (e.g. parts of a ConsString).
JSGCDumpEdge_TYPE_HIDDEN, // A link that is needed for proper sizes
// calculation, but may be hidden from user.
JSGCDumpEdge_TYPE_SHORTCUT, // A link that must not be followed during
// sizes calculation.
JSGCDumpEdge_TYPE_WEAK // A weak reference (ignored by the GC).
};

// edge types come from the v8's impl
enum {
JSGCDumpNode_TYPE_HIDDEN, // Hidden node, may be filtered when shown to
// user.
JSGCDumpNode_TYPE_ARRAY, // An array of elements.
JSGCDumpNode_TYPE_STRING, // A string.
JSGCDumpNode_TYPE_OBJECT, // A JS object (except for arrays and strings).
JSGCDumpNode_TYPE_CODE, // Compiled code.
JSGCDumpNode_TYPE_CLOSURE, // Function closure.
JSGCDumpNode_TYPE_REGEXP, // RegExp.
JSGCDumpNode_TYPE_HEAP_NUMBER, // Number stored in the heap.
JSGCDumpNode_TYPE_NATIVE, // Native object (not from V8 heap).
JSGCDumpNode_TYPE_SYNTHETIC, // Synthetic object, usually used for grouping
// snapshot items together.
JSGCDumpNode_TYPE_CONS_STRING, // Concatenated string. A pair of pointers
// to strings.
JSGCDumpNode_TYPE_SLICED_STRING, // Sliced string. A fragment of another
// string.
JSGCDumpNode_TYPE_SYMBOL, // A Symbol (ES6).
JSGCDumpNode_TYPE_BIGINT // BigInt.
};

typedef struct JSGCDumpNode JSGCDumpNode;
typedef struct JSGCDumpEdge JSGCDumpEdge;
typedef struct JSGCDumpContext JSGCDumpContext;

typedef struct JS_GCDumpFuncContext {
JSGCDumpContext *dc;

// prop info when traversing the parent object
JSShapeProperty *prs;
JSProperty *pr;

// manually specified prop name
union {
const char *n;
int i;
} p;
int plen; // -1: i, >0: n

int parent;
// user specified data
void *udata;
} JS_GCDumpFuncContext;

typedef void JS_GCDumpFunc(JSRuntime *rt, void *cell,
JS_GCDumpFuncContext dctx);
void JS_GCDumpValue(JSRuntime *rt, JSValueConst val, JS_GCDumpFunc *walk_func,
JS_GCDumpFuncContext dctx);

JSContext *JS_NewContext(JSRuntime *rt);
void JS_FreeContext(JSContext *s);
JSContext *JS_DupContext(JSContext *ctx);
Expand Down Expand Up @@ -478,6 +545,8 @@ typedef struct JSClassExoticMethods {
typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);
typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
typedef void JSClassGCDump(JSRuntime *rt, JSValueConst val,
JS_GCDumpFunc *walk_func, JS_GCDumpFuncContext dctx);
#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0)
typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_val, int argc, JSValueConst *argv,
Expand All @@ -487,6 +556,7 @@ typedef struct JSClassDef {
const char *class_name;
JSClassFinalizer *finalizer;
JSClassGCMark *gc_mark;
JSClassGCDump *gc_dump;
/* if call != NULL, the object is a function. If (flags &
JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a
constructor. In this case, 'this_val' is new.target. A
Expand Down
6 changes: 5 additions & 1 deletion libs/quickjs-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3606,7 +3606,11 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv) {

JS_SetPropertyStr(
ctx, global_obj, "__js_debug_pc2line",
JS_NewCFunction(ctx, js_debug_pc2line, "__js_debug_pc2line", 0));
JS_NewCFunction(ctx, js_debug_pc2line, "__js_debug_pc2line", 1));

JS_SetPropertyStr(
ctx, global_obj, "__js_gcdump_objects",
JS_NewCFunction(ctx, js_gcdump_objects, "__js_gcdump_objects", 0));

JS_FreeValue(ctx, global_obj);
}
Expand Down
Loading

0 comments on commit 4b8cc27

Please sign in to comment.