mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2024-11-23 12:19:46 +00:00
256 lines
8.8 KiB
C++
256 lines
8.8 KiB
C++
/*
|
|
* Copyright (C) 2015-2016 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "InspectorScriptProfilerAgent.h"
|
|
|
|
#include "Debugger.h"
|
|
#include "DeferGC.h"
|
|
#include "HeapInlines.h"
|
|
#include "InspectorEnvironment.h"
|
|
#include "SamplingProfiler.h"
|
|
#include <wtf/Stopwatch.h>
|
|
|
|
namespace Inspector {
|
|
|
|
using namespace JSC;
|
|
|
|
InspectorScriptProfilerAgent::InspectorScriptProfilerAgent(AgentContext& context)
|
|
: InspectorAgentBase("ScriptProfiler"_s)
|
|
, m_frontendDispatcher(makeUnique<ScriptProfilerFrontendDispatcher>(context.frontendRouter))
|
|
, m_backendDispatcher(ScriptProfilerBackendDispatcher::create(context.backendDispatcher, this))
|
|
, m_environment(context.environment)
|
|
{
|
|
}
|
|
|
|
InspectorScriptProfilerAgent::~InspectorScriptProfilerAgent() = default;
|
|
|
|
void InspectorScriptProfilerAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*)
|
|
{
|
|
}
|
|
|
|
void InspectorScriptProfilerAgent::willDestroyFrontendAndBackend(DisconnectReason)
|
|
{
|
|
// Stop tracking without sending results.
|
|
if (m_tracking) {
|
|
m_tracking = false;
|
|
m_activeEvaluateScript = false;
|
|
m_environment.debugger().setProfilingClient(nullptr);
|
|
|
|
// Stop sampling without processing the samples.
|
|
stopSamplingWhenDisconnecting();
|
|
}
|
|
}
|
|
|
|
Protocol::ErrorStringOr<void> InspectorScriptProfilerAgent::startTracking(Optional<bool>&& includeSamples)
|
|
{
|
|
if (m_tracking)
|
|
return { };
|
|
|
|
m_tracking = true;
|
|
|
|
auto& stopwatch = m_environment.executionStopwatch();
|
|
|
|
#if ENABLE(SAMPLING_PROFILER)
|
|
if (includeSamples && *includeSamples) {
|
|
VM& vm = m_environment.debugger().vm();
|
|
SamplingProfiler& samplingProfiler = vm.ensureSamplingProfiler(stopwatch);
|
|
|
|
LockHolder locker(samplingProfiler.getLock());
|
|
samplingProfiler.setStopWatch(locker, stopwatch);
|
|
samplingProfiler.noticeCurrentThreadAsJSCExecutionThread(locker);
|
|
samplingProfiler.start(locker);
|
|
m_enabledSamplingProfiler = true;
|
|
}
|
|
#else
|
|
UNUSED_PARAM(includeSamples);
|
|
#endif // ENABLE(SAMPLING_PROFILER)
|
|
|
|
m_environment.debugger().setProfilingClient(this);
|
|
|
|
m_frontendDispatcher->trackingStart(stopwatch.elapsedTime().seconds());
|
|
|
|
return { };
|
|
}
|
|
|
|
Protocol::ErrorStringOr<void> InspectorScriptProfilerAgent::stopTracking()
|
|
{
|
|
if (!m_tracking)
|
|
return { };
|
|
|
|
m_tracking = false;
|
|
m_activeEvaluateScript = false;
|
|
|
|
m_environment.debugger().setProfilingClient(nullptr);
|
|
|
|
trackingComplete();
|
|
|
|
return { };
|
|
}
|
|
|
|
bool InspectorScriptProfilerAgent::isAlreadyProfiling() const
|
|
{
|
|
return m_activeEvaluateScript;
|
|
}
|
|
|
|
Seconds InspectorScriptProfilerAgent::willEvaluateScript()
|
|
{
|
|
m_activeEvaluateScript = true;
|
|
|
|
#if ENABLE(SAMPLING_PROFILER)
|
|
if (m_enabledSamplingProfiler) {
|
|
SamplingProfiler* samplingProfiler = m_environment.debugger().vm().samplingProfiler();
|
|
RELEASE_ASSERT(samplingProfiler);
|
|
samplingProfiler->noticeCurrentThreadAsJSCExecutionThread();
|
|
}
|
|
#endif
|
|
|
|
return m_environment.executionStopwatch().elapsedTime();
|
|
}
|
|
|
|
void InspectorScriptProfilerAgent::didEvaluateScript(Seconds startTime, ProfilingReason reason)
|
|
{
|
|
m_activeEvaluateScript = false;
|
|
|
|
Seconds endTime = m_environment.executionStopwatch().elapsedTime();
|
|
|
|
addEvent(startTime, endTime, reason);
|
|
}
|
|
|
|
static Protocol::ScriptProfiler::EventType toProtocol(ProfilingReason reason)
|
|
{
|
|
switch (reason) {
|
|
case ProfilingReason::API:
|
|
return Protocol::ScriptProfiler::EventType::API;
|
|
case ProfilingReason::Microtask:
|
|
return Protocol::ScriptProfiler::EventType::Microtask;
|
|
case ProfilingReason::Other:
|
|
return Protocol::ScriptProfiler::EventType::Other;
|
|
}
|
|
|
|
ASSERT_NOT_REACHED();
|
|
return Protocol::ScriptProfiler::EventType::Other;
|
|
}
|
|
|
|
void InspectorScriptProfilerAgent::addEvent(Seconds startTime, Seconds endTime, ProfilingReason reason)
|
|
{
|
|
ASSERT(endTime >= startTime);
|
|
|
|
auto event = Protocol::ScriptProfiler::Event::create()
|
|
.setStartTime(startTime.seconds())
|
|
.setEndTime(endTime.seconds())
|
|
.setType(toProtocol(reason))
|
|
.release();
|
|
|
|
m_frontendDispatcher->trackingUpdate(WTFMove(event));
|
|
}
|
|
|
|
#if ENABLE(SAMPLING_PROFILER)
|
|
static Ref<Protocol::ScriptProfiler::Samples> buildSamples(VM& vm, Vector<SamplingProfiler::StackTrace>&& samplingProfilerStackTraces)
|
|
{
|
|
auto stackTraces = JSON::ArrayOf<Protocol::ScriptProfiler::StackTrace>::create();
|
|
for (SamplingProfiler::StackTrace& stackTrace : samplingProfilerStackTraces) {
|
|
auto frames = JSON::ArrayOf<Protocol::ScriptProfiler::StackFrame>::create();
|
|
for (SamplingProfiler::StackFrame& stackFrame : stackTrace.frames) {
|
|
auto frameObject = Protocol::ScriptProfiler::StackFrame::create()
|
|
.setSourceID(String::number(stackFrame.sourceID()))
|
|
.setName(stackFrame.displayName(vm))
|
|
.setLine(stackFrame.functionStartLine())
|
|
.setColumn(stackFrame.functionStartColumn())
|
|
.setUrl(stackFrame.url())
|
|
.release();
|
|
|
|
if (stackFrame.hasExpressionInfo()) {
|
|
Ref<Protocol::ScriptProfiler::ExpressionLocation> expressionLocation = Protocol::ScriptProfiler::ExpressionLocation::create()
|
|
.setLine(stackFrame.lineNumber())
|
|
.setColumn(stackFrame.columnNumber())
|
|
.release();
|
|
frameObject->setExpressionLocation(WTFMove(expressionLocation));
|
|
}
|
|
|
|
frames->addItem(WTFMove(frameObject));
|
|
}
|
|
Ref<Protocol::ScriptProfiler::StackTrace> inspectorStackTrace = Protocol::ScriptProfiler::StackTrace::create()
|
|
.setTimestamp(stackTrace.timestamp.seconds())
|
|
.setStackFrames(WTFMove(frames))
|
|
.release();
|
|
stackTraces->addItem(WTFMove(inspectorStackTrace));
|
|
}
|
|
|
|
return Protocol::ScriptProfiler::Samples::create()
|
|
.setStackTraces(WTFMove(stackTraces))
|
|
.release();
|
|
}
|
|
#endif // ENABLE(SAMPLING_PROFILER)
|
|
|
|
void InspectorScriptProfilerAgent::trackingComplete()
|
|
{
|
|
auto timestamp = m_environment.executionStopwatch().elapsedTime().seconds();
|
|
|
|
#if ENABLE(SAMPLING_PROFILER)
|
|
if (m_enabledSamplingProfiler) {
|
|
VM& vm = m_environment.debugger().vm();
|
|
JSLockHolder lock(vm);
|
|
DeferGC deferGC(vm.heap); // This is required because we will have raw pointers into the heap after we releaseStackTraces().
|
|
SamplingProfiler* samplingProfiler = vm.samplingProfiler();
|
|
RELEASE_ASSERT(samplingProfiler);
|
|
|
|
LockHolder locker(samplingProfiler->getLock());
|
|
samplingProfiler->pause(locker);
|
|
Vector<SamplingProfiler::StackTrace> stackTraces = samplingProfiler->releaseStackTraces(locker);
|
|
locker.unlockEarly();
|
|
|
|
Ref<Protocol::ScriptProfiler::Samples> samples = buildSamples(vm, WTFMove(stackTraces));
|
|
|
|
m_enabledSamplingProfiler = false;
|
|
|
|
m_frontendDispatcher->trackingComplete(timestamp, WTFMove(samples));
|
|
} else
|
|
m_frontendDispatcher->trackingComplete(timestamp, nullptr);
|
|
#else
|
|
m_frontendDispatcher->trackingComplete(timestamp, nullptr);
|
|
#endif // ENABLE(SAMPLING_PROFILER)
|
|
}
|
|
|
|
void InspectorScriptProfilerAgent::stopSamplingWhenDisconnecting()
|
|
{
|
|
#if ENABLE(SAMPLING_PROFILER)
|
|
if (!m_enabledSamplingProfiler)
|
|
return;
|
|
|
|
VM& vm = m_environment.debugger().vm();
|
|
JSLockHolder lock(vm);
|
|
SamplingProfiler* samplingProfiler = vm.samplingProfiler();
|
|
RELEASE_ASSERT(samplingProfiler);
|
|
LockHolder locker(samplingProfiler->getLock());
|
|
samplingProfiler->pause(locker);
|
|
samplingProfiler->clearData(locker);
|
|
|
|
m_enabledSamplingProfiler = false;
|
|
#endif
|
|
}
|
|
|
|
} // namespace Inspector
|