aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-13 20:47:51 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-13 20:47:51 -0800
commitf61d9e340d35f982e8ad86b705af46464c7f1d2a (patch)
treeeb89549abd0be7f7c63ce8c4eee8127dcca81acf
parent47a2548cbfbb977e9b1f0c749bd83d47b4c4596b (diff)
downloadbun-f61d9e340d35f982e8ad86b705af46464c7f1d2a.tar.gz
bun-f61d9e340d35f982e8ad86b705af46464c7f1d2a.tar.zst
bun-f61d9e340d35f982e8ad86b705af46464c7f1d2a.zip
[bun:jsc] Introduce `profile` function
-rw-r--r--packages/bun-types/jsc.d.ts108
-rw-r--r--src/bun.js/bindings/BunJSCModule.cpp53
-rw-r--r--src/bun.js/bun-jsc.exports.js1
3 files changed, 162 insertions, 0 deletions
diff --git a/packages/bun-types/jsc.d.ts b/packages/bun-types/jsc.d.ts
index 30c7caebd..b909d0348 100644
--- a/packages/bun-types/jsc.d.ts
+++ b/packages/bun-types/jsc.d.ts
@@ -37,6 +37,114 @@ declare module "bun:jsc" {
export function drainMicrotasks(): void;
/**
+ * Run JavaScriptCore's sampling profiler for a particular function
+ *
+ * This is pretty low-level.
+ *
+ * Things to know:
+ * - LLint means "Low Level Interpreter", which is the interpreter that runs before any JIT compilation
+ * - Baseline is the first JIT compilation tier. It's the least optimized, but the fastest to compile
+ * - DFG means "Data Flow Graph", which is the second JIT compilation tier. It has some optimizations, but is slower to compile
+ * - FTL means "Faster Than Light", which is the third JIT compilation tier. It has the most optimizations, but is the slowest to compile
+ */
+ export function profile(
+ callback: CallableFunction,
+ sampleInterval?: number,
+ ): {
+ /**
+ * A formatted summary of the top functions
+ *
+ * Example output:
+ * ```js
+ *
+ * Sampling rate: 100.000000 microseconds. Total samples: 6858
+ * Top functions as <numSamples 'functionName#hash:sourceID'>
+ * 2948 '#<nil>:8'
+ * 393 'visit#<nil>:8'
+ * 263 'push#<nil>:8'
+ * 164 'scan_ref_scoped#<nil>:8'
+ * 164 'walk#<nil>:8'
+ * 144 'pop#<nil>:8'
+ * 107 'extract_candidates#<nil>:8'
+ * 94 'get#<nil>:8'
+ * 82 'Function#<nil>:4294967295'
+ * 79 'set#<nil>:8'
+ * 67 'forEach#<nil>:5'
+ * 58 'collapse#<nil>:8'
+ * ```
+ */
+ functions: string;
+ /**
+ * A formatted summary of the top bytecodes
+ *
+ * Example output:
+ * ```js
+ * Tier breakdown:
+ * -----------------------------------
+ * LLInt: 106 (1.545640%)
+ * Baseline: 2355 (34.339458%)
+ * DFG: 3290 (47.973170%)
+ * FTL: 833 (12.146398%)
+ * js builtin: 132 (1.924759%)
+ * Wasm: 0 (0.000000%)
+ * Host: 111 (1.618548%)
+ * RegExp: 15 (0.218723%)
+ * C/C++: 0 (0.000000%)
+ * Unknown Executable: 148 (2.158064%)
+ *
+ *
+ * Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>
+ * 273 'visit#<nil>:DFG:bc#63'
+ * 121 'walk#<nil>:DFG:bc#7'
+ * 119 '#<nil>:Baseline:bc#1'
+ * 82 'Function#<nil>:None:<nil>'
+ * 66 '#<nil>:DFG:bc#11'
+ * 65 '#<nil>:DFG:bc#33'
+ * 58 '#<nil>:Baseline:bc#7'
+ * 53 '#<nil>:Baseline:bc#23'
+ * 50 'forEach#<nil>:DFG:bc#83'
+ * 49 'pop#<nil>:FTL:bc#65'
+ * 47 '#<nil>:DFG:bc#99'
+ * 45 '#<nil>:DFG:bc#16'
+ * 44 '#<nil>:DFG:bc#7'
+ * 44 '#<nil>:Baseline:bc#30'
+ * 44 'push#<nil>:FTL:bc#214'
+ * 41 '#<nil>:DFG:bc#50'
+ * 39 'get#<nil>:DFG:bc#27'
+ * 39 '#<nil>:Baseline:bc#0'
+ * 36 '#<nil>:DFG:bc#27'
+ * 36 'Dictionary#<nil>:DFG:bc#41'
+ * 36 'visit#<nil>:DFG:bc#81'
+ * 36 'get#<nil>:FTL:bc#11'
+ * 32 'push#<nil>:FTL:bc#49'
+ * 31 '#<nil>:DFG:bc#76'
+ * 31 '#<nil>:DFG:bc#10'
+ * 31 '#<nil>:DFG:bc#73'
+ * 29 'set#<nil>:DFG:bc#28'
+ * 28 'in_boolean_context#<nil>:DFG:bc#104'
+ * 28 '#<nil>:Baseline:<nil>'
+ * 28 'regExpSplitFast#<nil>:None:<nil>'
+ * 26 'visit#<nil>:DFG:bc#95'
+ * 26 'pop#<nil>:FTL:bc#120'
+ * 25 '#<nil>:DFG:bc#23'
+ * 25 'push#<nil>:FTL:bc#152'
+ * 24 'push#<nil>:FTL:bc#262'
+ * 24 '#<nil>:FTL:bc#10'
+ * 23 'is_identifier_char#<nil>:DFG:bc#22'
+ * 23 'visit#<nil>:DFG:bc#22'
+ * 22 '#<nil>:FTL:bc#27'
+ * 22 'indexOf#<nil>:None:<nil>'
+ * ```
+ */
+ bytecodes: string;
+
+ /**
+ * Stack traces of the top functions
+ */
+ stackTraces: string[];
+ };
+
+ /**
* This returns objects which native code has explicitly protected from being
* garbage collected
*
diff --git a/src/bun.js/bindings/BunJSCModule.cpp b/src/bun.js/bindings/BunJSCModule.cpp
index fd92b817e..384882883 100644
--- a/src/bun.js/bindings/BunJSCModule.cpp
+++ b/src/bun.js/bindings/BunJSCModule.cpp
@@ -421,6 +421,58 @@ JSC_DEFINE_HOST_FUNCTION(functionDrainMicrotasks, (JSGlobalObject * globalObject
return JSValue::encode(jsUndefined());
}
+JSC_DEFINE_HOST_FUNCTION(functionRunProfiler, (JSGlobalObject * globalObject, CallFrame* callFrame))
+{
+ JSC::VM& vm = globalObject->vm();
+ JSC::SamplingProfiler& samplingProfiler = vm.ensureSamplingProfiler(WTF::Stopwatch::create());
+
+ JSC::JSValue callbackValue = callFrame->argument(0);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ if (callbackValue.isUndefinedOrNull() || !callbackValue.isCallable()) {
+ throwException(globalObject, throwScope, createTypeError(globalObject, "First argument must be a function."_s));
+ return JSValue::encode(JSValue {});
+ }
+
+ JSC::JSFunction* function = jsCast<JSC::JSFunction*>(callbackValue);
+
+ JSC::JSValue sampleValue = callFrame->argument(1);
+ if (sampleValue.isNumber()) {
+ unsigned sampleInterval = sampleValue.toUInt32(globalObject);
+ samplingProfiler.setTimingInterval(Seconds::fromMicroseconds(sampleInterval));
+ }
+
+ JSC::CallData callData = JSC::getCallData(function);
+ MarkedArgumentBuffer args;
+
+ samplingProfiler.noticeCurrentThreadAsJSCExecutionThread();
+ samplingProfiler.start();
+ JSC::call(globalObject, function, callData, JSC::jsUndefined(), args);
+ samplingProfiler.pause();
+ if (throwScope.exception()) {
+ samplingProfiler.shutdown();
+ samplingProfiler.clearData();
+ return JSValue::encode(JSValue {});
+ }
+
+ StringPrintStream topFunctions;
+ samplingProfiler.reportTopFunctions(topFunctions);
+
+ StringPrintStream byteCodes;
+ samplingProfiler.reportTopBytecodes(byteCodes);
+
+ JSValue stackTraces = JSONParse(globalObject, samplingProfiler.stackTracesAsJSON());
+
+ samplingProfiler.shutdown();
+ samplingProfiler.clearData();
+
+ JSObject* result = constructEmptyObject(globalObject, globalObject->objectPrototype(), 3);
+ result->putDirect(vm, Identifier::fromString(vm, "functions"_s), jsString(vm, topFunctions.toString()));
+ result->putDirect(vm, Identifier::fromString(vm, "bytecodes"_s), jsString(vm, byteCodes.toString()));
+ result->putDirect(vm, Identifier::fromString(vm, "stackTraces"_s), stackTraces);
+
+ return JSValue::encode(result);
+}
+
JSC_DECLARE_HOST_FUNCTION(functionGenerateHeapSnapshotForDebugging);
JSC_DEFINE_HOST_FUNCTION(functionGenerateHeapSnapshotForDebugging, (JSGlobalObject * globalObject, CallFrame*))
{
@@ -476,6 +528,7 @@ JSC::JSObject* createJSCModule(JSC::JSGlobalObject* globalObject)
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "totalCompileTime"_s), 1, functionTotalCompileTime, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getProtectedObjects"_s), 1, functionGetProtectedObjects, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "generateHeapSnapshotForDebugging"_s), 0, functionGenerateHeapSnapshotForDebugging, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "profile"_s), 0, functionRunProfiler, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
}
return object;
diff --git a/src/bun.js/bun-jsc.exports.js b/src/bun.js/bun-jsc.exports.js
index ee758a3b3..b35a5e7a6 100644
--- a/src/bun.js/bun-jsc.exports.js
+++ b/src/bun.js/bun-jsc.exports.js
@@ -27,4 +27,5 @@ export const totalCompileTime = jsc.totalCompileTime;
export const getProtectedObjects = jsc.getProtectedObjects;
export const generateHeapSnapshotForDebugging =
jsc.generateHeapSnapshotForDebugging;
+export const profile = jsc.profile;
export default jsc;