Skip to content

Commit

Permalink
[JSC] Add dumpAndClearSamplingProfilerSamples function
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=259411
rdar://112686960

Reviewed by Justin Michaud.

This patch adds dumpAndClearSamplingProfilerSamples behind a flag, which dumps sampling profiler data into temp file.
This allows us to automated sampling data collection for benchmarks by calling this function at the right timing.
We also significantly expand the JSON data generation of SamplingProfiler to reconstruct enough information from this output.

We also attach display-sampling-profiler-output script as the same to display-profiler-output. Which can feed the above JSON
and dump sampling profiler output.

* Source/JavaScriptCore/runtime/JSGlobalObject.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::JSGlobalObject::init):
* Source/JavaScriptCore/runtime/VM.cpp:
(JSC::VM::enableSamplingProfiler):
(JSC::VM::disableSamplingProfiler):
(JSC::VM::takeSamplingProfilerSamplesAsJSONString):
* Source/JavaScriptCore/runtime/VM.h:

Canonical link: https://commits.webkit.org/266270@main
  • Loading branch information
Constellation committed Jul 24, 2023
1 parent e812d68 commit cc1dd7f
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 203 deletions.
2 changes: 1 addition & 1 deletion JSTests/stress/sampling-profiler-anonymous-function.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ if (platformSupportsSamplingProfiler()) {
});
}

runTest(baz, ["(anonymous function)", "foo", "baz"]);
runTest(baz, ["", "foo", "baz"]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if (platformSupportsSamplingProfiler()) {
for (let i = 0; i < 1000; ++i) {
foo();
let stacktraces = samplingProfilerStackTraces();
for (let stackTrace of stacktraces) { }
for (let stackTrace of stacktraces.traces) { }
}
}

Expand Down
6 changes: 3 additions & 3 deletions JSTests/stress/sampling-profiler/samplingProfiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ function makeNode(name) {

function updateCallingContextTree(root) {
let stacktraces = samplingProfilerStackTraces();
for (let stackTrace of stacktraces) {
for (let stackTrace of stacktraces.traces) {
let node = root;
for (let i = stackTrace.length; i--; ) {
let functionName = stackTrace[i];
for (let i = stackTrace.frames.length; i--; ) {
let functionName = stackTrace.frames[i].name;
node = node.makeChildIfNeeded(functionName);
}
}
Expand Down
5 changes: 3 additions & 2 deletions Source/JavaScriptCore/jsc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2976,8 +2976,9 @@ JSC_DEFINE_HOST_FUNCTION(functionSamplingProfilerStackTraces, (JSGlobalObject* g
if (!vm.samplingProfiler())
return JSValue::encode(throwException(globalObject, scope, createError(globalObject, "Sampling profiler was never started"_s)));

String jsonString = vm.samplingProfiler()->stackTracesAsJSON();
EncodedJSValue result = JSValue::encode(JSONParse(globalObject, jsonString));
auto json = vm.samplingProfiler()->stackTracesAsJSON();
auto jsonString = json->toJSONString();
EncodedJSValue result = JSValue::encode(JSONParse(globalObject, WTFMove(jsonString)));
scope.releaseAssertNoException();
return result;
}
Expand Down
44 changes: 35 additions & 9 deletions Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ static JSC_DECLARE_HOST_FUNCTION(assertCall);
#if ENABLE(SAMPLING_PROFILER)
static JSC_DECLARE_HOST_FUNCTION(enableSamplingProfiler);
static JSC_DECLARE_HOST_FUNCTION(disableSamplingProfiler);
static JSC_DECLARE_HOST_FUNCTION(dumpAndClearSamplingProfilerSamples);
#endif

static JSC_DECLARE_HOST_FUNCTION(tracePointStart);
Expand Down Expand Up @@ -405,22 +406,46 @@ JSC_DEFINE_HOST_FUNCTION(assertCall, (JSGlobalObject* globalObject, CallFrame* c
#if ENABLE(SAMPLING_PROFILER)
JSC_DEFINE_HOST_FUNCTION(enableSamplingProfiler, (JSGlobalObject* globalObject, CallFrame*))
{
SamplingProfiler* profiler = globalObject->vm().samplingProfiler();
if (!profiler)
profiler = &globalObject->vm().ensureSamplingProfiler(Stopwatch::create());
profiler->start();
globalObject->vm().enableSamplingProfiler();
return JSValue::encode(jsUndefined());
}

JSC_DEFINE_HOST_FUNCTION(disableSamplingProfiler, (JSGlobalObject* globalObject, CallFrame*))
{
SamplingProfiler* profiler = globalObject->vm().samplingProfiler();
if (!profiler)
profiler = &globalObject->vm().ensureSamplingProfiler(Stopwatch::create());
globalObject->vm().disableSamplingProfiler();
return JSValue::encode(jsUndefined());
}

JSC_DEFINE_HOST_FUNCTION(dumpAndClearSamplingProfilerSamples, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);

JSValue argument = callFrame->argument(0);
auto filenamePrefix = emptyString();
if (!argument.isUndefinedOrNull()) {
filenamePrefix = argument.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, { });
}

auto json = vm.takeSamplingProfilerSamplesAsJSON();
if (UNLIKELY(!json))
return JSValue::encode(jsUndefined());

auto jsonData = json->toJSONString();
{
Locker locker { profiler->getLock() };
profiler->pause();
FileSystem::PlatformFileHandle fileHandle;
String tempFilePath = FileSystem::openTemporaryFile(filenamePrefix, fileHandle);
if (!FileSystem::isHandleValid(fileHandle)) {
dataLogLn("Dumping sampling profiler samples failed to open temporary file");
return JSValue::encode(jsUndefined());
}

CString utf8String = jsonData.utf8();

FileSystem::writeToFile(fileHandle, utf8String.data(), utf8String.length());
FileSystem::closeFile(fileHandle);
dataLogLn("Dumped sampling profiler samples to ", tempFilePath);
}

return JSValue::encode(jsUndefined());
Expand Down Expand Up @@ -1659,6 +1684,7 @@ capitalName ## Constructor* lowerName ## Constructor = featureFlag ? capitalName
#if ENABLE(SAMPLING_PROFILER)
putDirectWithoutTransition(vm, Identifier::fromString(vm, "__enableSamplingProfiler"_s), JSFunction::create(vm, this, 1, "enableSamplingProfiler"_s, enableSamplingProfiler, ImplementationVisibility::Public), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
putDirectWithoutTransition(vm, Identifier::fromString(vm, "__disableSamplingProfiler"_s), JSFunction::create(vm, this, 1, "disableSamplingProfiler"_s, disableSamplingProfiler, ImplementationVisibility::Public), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
putDirectWithoutTransition(vm, Identifier::fromString(vm, "__dumpAndClearSamplingProfilerSamples"_s), JSFunction::create(vm, this, 1, "dumpAndClearSamplingProfilerSamples"_s, dumpAndClearSamplingProfilerSamples, ImplementationVisibility::Public), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
#endif
putDirectWithoutTransition(vm, Identifier::fromString(vm, "__enableSuperSampler"_s), JSFunction::create(vm, this, 1, "enableSuperSampler"_s, enableSuperSampler, ImplementationVisibility::Public), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
putDirectWithoutTransition(vm, Identifier::fromString(vm, "__disableSuperSampler"_s), JSFunction::create(vm, this, 1, "disableSuperSampler"_s, disableSuperSampler, ImplementationVisibility::Public), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
Expand Down

0 comments on commit cc1dd7f

Please sign in to comment.