InspectorScriptProfilerAgent.cpp [plain text]
#include "config.h"
#include "InspectorScriptProfilerAgent.h"
#include "DeferGC.h"
#include "HeapInlines.h"
#include "InspectorEnvironment.h"
#include "SamplingProfiler.h"
#include "ScriptDebugServer.h"
#include <wtf/Stopwatch.h>
using namespace JSC;
namespace Inspector {
InspectorScriptProfilerAgent::InspectorScriptProfilerAgent(AgentContext& context)
: InspectorAgentBase(ASCIILiteral("ScriptProfiler"))
, m_frontendDispatcher(std::make_unique<ScriptProfilerFrontendDispatcher>(context.frontendRouter))
, m_backendDispatcher(ScriptProfilerBackendDispatcher::create(context.backendDispatcher, this))
, m_environment(context.environment)
{
}
InspectorScriptProfilerAgent::~InspectorScriptProfilerAgent()
{
}
void InspectorScriptProfilerAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*)
{
}
void InspectorScriptProfilerAgent::willDestroyFrontendAndBackend(DisconnectReason)
{
if (m_tracking) {
m_tracking = false;
m_activeEvaluateScript = false;
m_environment.scriptDebugServer().setProfilingClient(nullptr);
stopSamplingWhenDisconnecting();
}
}
void InspectorScriptProfilerAgent::startTracking(ErrorString&, const bool* const includeSamples)
{
if (m_tracking)
return;
m_tracking = true;
#if ENABLE(SAMPLING_PROFILER)
if (includeSamples && *includeSamples) {
VM& vm = m_environment.scriptDebugServer().vm();
SamplingProfiler& samplingProfiler = vm.ensureSamplingProfiler(m_environment.executionStopwatch());
LockHolder locker(samplingProfiler.getLock());
samplingProfiler.setStopWatch(locker, m_environment.executionStopwatch());
samplingProfiler.noticeCurrentThreadAsJSCExecutionThread(locker);
samplingProfiler.start(locker);
m_enabledSamplingProfiler = true;
}
#else
UNUSED_PARAM(includeSamples);
#endif // ENABLE(SAMPLING_PROFILER)
m_environment.scriptDebugServer().setProfilingClient(this);
m_frontendDispatcher->trackingStart(m_environment.executionStopwatch()->elapsedTime());
}
void InspectorScriptProfilerAgent::stopTracking(ErrorString&)
{
if (!m_tracking)
return;
m_tracking = false;
m_activeEvaluateScript = false;
m_environment.scriptDebugServer().setProfilingClient(nullptr);
trackingComplete();
}
bool InspectorScriptProfilerAgent::isAlreadyProfiling() const
{
return m_activeEvaluateScript;
}
double InspectorScriptProfilerAgent::willEvaluateScript()
{
m_activeEvaluateScript = true;
#if ENABLE(SAMPLING_PROFILER)
if (m_enabledSamplingProfiler) {
SamplingProfiler* samplingProfiler = m_environment.scriptDebugServer().vm().samplingProfiler();
RELEASE_ASSERT(samplingProfiler);
samplingProfiler->noticeCurrentThreadAsJSCExecutionThread();
}
#endif
return m_environment.executionStopwatch()->elapsedTime();
}
void InspectorScriptProfilerAgent::didEvaluateScript(double startTime, ProfilingReason reason)
{
m_activeEvaluateScript = false;
double endTime = m_environment.executionStopwatch()->elapsedTime();
addEvent(startTime, endTime, reason);
}
static Inspector::Protocol::ScriptProfiler::EventType toProtocol(ProfilingReason reason)
{
switch (reason) {
case ProfilingReason::API:
return Inspector::Protocol::ScriptProfiler::EventType::API;
case ProfilingReason::Microtask:
return Inspector::Protocol::ScriptProfiler::EventType::Microtask;
case ProfilingReason::Other:
return Inspector::Protocol::ScriptProfiler::EventType::Other;
}
ASSERT_NOT_REACHED();
return Inspector::Protocol::ScriptProfiler::EventType::Other;
}
void InspectorScriptProfilerAgent::addEvent(double startTime, double endTime, ProfilingReason reason)
{
ASSERT(endTime >= startTime);
auto event = Inspector::Protocol::ScriptProfiler::Event::create()
.setStartTime(startTime)
.setEndTime(endTime)
.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)
.setStackFrames(WTFMove(frames))
.release();
stackTraces->addItem(WTFMove(inspectorStackTrace));
}
return Protocol::ScriptProfiler::Samples::create()
.setStackTraces(WTFMove(stackTraces))
.release();
}
#endif // ENABLE(SAMPLING_PROFILER)
void InspectorScriptProfilerAgent::trackingComplete()
{
#if ENABLE(SAMPLING_PROFILER)
if (m_enabledSamplingProfiler) {
VM& vm = m_environment.scriptDebugServer().vm();
JSLockHolder lock(vm);
DeferGC deferGC(vm.heap);
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(WTFMove(samples));
} else
m_frontendDispatcher->trackingComplete(nullptr);
#else
m_frontendDispatcher->trackingComplete(nullptr);
#endif // ENABLE(SAMPLING_PROFILER)
}
void InspectorScriptProfilerAgent::stopSamplingWhenDisconnecting()
{
#if ENABLE(SAMPLING_PROFILER)
if (!m_enabledSamplingProfiler)
return;
VM& vm = m_environment.scriptDebugServer().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
}
void InspectorScriptProfilerAgent::programmaticCaptureStarted()
{
m_frontendDispatcher->programmaticCaptureStarted();
}
void InspectorScriptProfilerAgent::programmaticCaptureStopped()
{
m_frontendDispatcher->programmaticCaptureStopped();
}
}