mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1050500 - Add callee information to Javascript timeline markers. r=smaug
This commit is contained in:
parent
fe2b6d5e46
commit
f0669fbdd0
@ -22,7 +22,8 @@ TimelineMarker::TimelineMarker(nsDocShell* aDocShell, const char* aName,
|
||||
|
||||
TimelineMarker::TimelineMarker(nsDocShell* aDocShell, const char* aName,
|
||||
TracingMetadata aMetaData,
|
||||
const nsAString& aCause)
|
||||
const nsAString& aCause,
|
||||
TimelineStackRequest aStackRequest)
|
||||
: mName(aName)
|
||||
, mMetaData(aMetaData)
|
||||
, mCause(aCause)
|
||||
@ -30,7 +31,7 @@ TimelineMarker::TimelineMarker(nsDocShell* aDocShell, const char* aName,
|
||||
MOZ_COUNT_CTOR(TimelineMarker);
|
||||
MOZ_ASSERT(aName);
|
||||
aDocShell->Now(&mTime);
|
||||
if (aMetaData == TRACING_INTERVAL_START) {
|
||||
if (aMetaData == TRACING_INTERVAL_START && aStackRequest != NO_STACK) {
|
||||
CaptureStack();
|
||||
}
|
||||
}
|
||||
|
@ -21,12 +21,15 @@ class nsDocShell;
|
||||
class TimelineMarker
|
||||
{
|
||||
public:
|
||||
enum TimelineStackRequest { STACK, NO_STACK };
|
||||
|
||||
TimelineMarker(nsDocShell* aDocShell, const char* aName,
|
||||
TracingMetadata aMetaData);
|
||||
|
||||
TimelineMarker(nsDocShell* aDocShell, const char* aName,
|
||||
TracingMetadata aMetaData,
|
||||
const nsAString& aCause);
|
||||
const nsAString& aCause,
|
||||
TimelineStackRequest aStackRequest = STACK);
|
||||
|
||||
virtual ~TimelineMarker();
|
||||
|
||||
@ -43,7 +46,9 @@ public:
|
||||
// called on both the starting and ending markers of a pair.
|
||||
// Ordinarily the ending marker doesn't need to do anything
|
||||
// here.
|
||||
virtual void AddDetails(mozilla::dom::ProfileTimelineMarker& aMarker) {}
|
||||
virtual void AddDetails(JSContext* aCx,
|
||||
mozilla::dom::ProfileTimelineMarker& aMarker)
|
||||
{}
|
||||
|
||||
virtual void AddLayerRectangles(
|
||||
mozilla::dom::Sequence<mozilla::dom::ProfileTimelineLayerRect>&)
|
||||
|
@ -2997,7 +2997,7 @@ nsDocShell::PopProfileTimelineMarkers(
|
||||
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
|
||||
marker->mStart = startPayload->GetTime();
|
||||
marker->mEnd = startPayload->GetTime();
|
||||
startPayload->AddDetails(*marker);
|
||||
startPayload->AddDetails(aCx, *marker);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -3045,8 +3045,8 @@ nsDocShell::PopProfileTimelineMarkers(
|
||||
if (isPaint) {
|
||||
marker->mRectangles.Construct(layerRectangles);
|
||||
}
|
||||
startPayload->AddDetails(*marker);
|
||||
endPayload->AddDetails(*marker);
|
||||
startPayload->AddDetails(aCx, *marker);
|
||||
endPayload->AddDetails(aCx, *marker);
|
||||
}
|
||||
|
||||
// We want the start to be dropped either way.
|
||||
@ -13912,27 +13912,61 @@ class JavascriptTimelineMarker : public TimelineMarker
|
||||
{
|
||||
public:
|
||||
JavascriptTimelineMarker(nsDocShell* aDocShell, const char* aName,
|
||||
const char* aReason)
|
||||
const char* aReason,
|
||||
const char16_t* aFunctionName,
|
||||
const char16_t* aFileName,
|
||||
uint32_t aLineNumber)
|
||||
: TimelineMarker(aDocShell, aName, TRACING_INTERVAL_START,
|
||||
NS_ConvertUTF8toUTF16(aReason))
|
||||
NS_ConvertUTF8toUTF16(aReason),
|
||||
NO_STACK)
|
||||
, mFunctionName(aFunctionName)
|
||||
, mFileName(aFileName)
|
||||
, mLineNumber(aLineNumber)
|
||||
{
|
||||
}
|
||||
|
||||
void AddDetails(mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
void AddDetails(JSContext* aCx, mozilla::dom::ProfileTimelineMarker& aMarker)
|
||||
override
|
||||
{
|
||||
aMarker.mCauseName.Construct(GetCause());
|
||||
|
||||
if (!mFunctionName.IsEmpty() || !mFileName.IsEmpty()) {
|
||||
ProfileTimelineStackFrame stackFrame;
|
||||
stackFrame.mLine.Construct(mLineNumber);
|
||||
stackFrame.mSource.Construct(mFileName);
|
||||
stackFrame.mFunctionDisplayName.Construct(mFunctionName);
|
||||
|
||||
JS::Rooted<JS::Value> newStack(aCx);
|
||||
if (ToJSValue(aCx, stackFrame, &newStack)) {
|
||||
if (newStack.isObject()) {
|
||||
aMarker.mStack = &newStack.toObject();
|
||||
}
|
||||
} else {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mFunctionName;
|
||||
nsString mFileName;
|
||||
uint32_t mLineNumber;
|
||||
};
|
||||
|
||||
void
|
||||
nsDocShell::NotifyJSRunToCompletionStart(const char* aReason)
|
||||
nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
|
||||
const char16_t* aFunctionName,
|
||||
const char16_t* aFilename,
|
||||
const uint32_t aLineNumber)
|
||||
{
|
||||
bool timelineOn = nsIDocShell::GetRecordProfileTimelineMarkers();
|
||||
|
||||
// If first start, mark interval start.
|
||||
if (timelineOn && mJSRunToCompletionDepth == 0) {
|
||||
mozilla::UniquePtr<TimelineMarker> marker =
|
||||
MakeUnique<JavascriptTimelineMarker>(this, "Javascript", aReason);
|
||||
MakeUnique<JavascriptTimelineMarker>(this, "Javascript", aReason,
|
||||
aFunctionName, aFilename,
|
||||
aLineNumber);
|
||||
AddProfileTimelineMarker(Move(marker));
|
||||
}
|
||||
mJSRunToCompletionDepth++;
|
||||
|
@ -54,7 +54,7 @@ interface nsITabParent;
|
||||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(bf78de98-9e88-498d-bc19-0e138f683939)]
|
||||
[scriptable, builtinclass, uuid(696b32a1-3cf1-4909-b501-474b25fc7954)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
@ -1039,7 +1039,10 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
* that execution has stopped. This only occurs when the Timeline devtool
|
||||
* is collecting information.
|
||||
*/
|
||||
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStart(in string aReason);
|
||||
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStart(in string aReason,
|
||||
in wstring functionName,
|
||||
in wstring fileName,
|
||||
in unsigned long lineNumber);
|
||||
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStop();
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,8 @@ let TESTS = [{
|
||||
// cause.
|
||||
let jsMarkers = markers.filter(m => m.name == "Javascript" && m.causeName);
|
||||
ok(jsMarkers.length > 0, "Got some Javascript markers");
|
||||
is(jsMarkers[0].stack.functionDisplayName, "do_xhr",
|
||||
"Javascript marker has entry point name");
|
||||
}
|
||||
}];
|
||||
|
||||
|
@ -1019,7 +1019,8 @@ public:
|
||||
return GetCause() == aOther.GetCause();
|
||||
}
|
||||
|
||||
virtual void AddDetails(mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
virtual void AddDetails(JSContext* aCx,
|
||||
mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
{
|
||||
if (GetMetaData() == TRACING_INTERVAL_START) {
|
||||
aMarker.mCauseName.Construct(GetCause());
|
||||
@ -1041,7 +1042,8 @@ public:
|
||||
MOZ_ASSERT(aMetaData == TRACING_TIMESTAMP);
|
||||
}
|
||||
|
||||
virtual void AddDetails(mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
virtual void AddDetails(JSContext* aCx,
|
||||
mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
{
|
||||
if (!GetCause().IsEmpty()) {
|
||||
aMarker.mCauseName.Construct(GetCause());
|
||||
|
@ -529,7 +529,6 @@ AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
|
||||
aCx ? aCx : FindJSContext(aGlobalObject))
|
||||
, ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
|
||||
, mWebIDLCallerPrincipal(nullptr)
|
||||
, mDocShellForJSRunToCompletion(nullptr)
|
||||
, mIsMainThread(aIsMainThread)
|
||||
{
|
||||
MOZ_ASSERT(aGlobalObject);
|
||||
@ -540,23 +539,12 @@ AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
|
||||
}
|
||||
|
||||
if (aIsMainThread && gRunToCompletionListeners > 0) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobalObject);
|
||||
if (window) {
|
||||
mDocShellForJSRunToCompletion = window->GetDocShell();
|
||||
}
|
||||
}
|
||||
|
||||
if (mDocShellForJSRunToCompletion) {
|
||||
mDocShellForJSRunToCompletion->NotifyJSRunToCompletionStart(aReason);
|
||||
mDocShellEntryMonitor.emplace(cx(), aReason);
|
||||
}
|
||||
}
|
||||
|
||||
AutoEntryScript::~AutoEntryScript()
|
||||
{
|
||||
if (mDocShellForJSRunToCompletion) {
|
||||
mDocShellForJSRunToCompletion->NotifyJSRunToCompletionStop();
|
||||
}
|
||||
|
||||
if (mIsMainThread) {
|
||||
nsContentUtils::LeaveMicroTask();
|
||||
}
|
||||
@ -567,6 +555,76 @@ AutoEntryScript::~AutoEntryScript()
|
||||
JS_MaybeGC(cx());
|
||||
}
|
||||
|
||||
AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
|
||||
const char* aReason)
|
||||
: JS::dbg::AutoEntryMonitor(aCx)
|
||||
, mReason(aReason)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFunction,
|
||||
JSScript* aScript)
|
||||
{
|
||||
JS::Rooted<JSFunction*> rootedFunction(aCx);
|
||||
if (aFunction) {
|
||||
rootedFunction = aFunction;
|
||||
}
|
||||
JS::Rooted<JSScript*> rootedScript(aCx);
|
||||
if (aScript) {
|
||||
rootedScript = aScript;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window =
|
||||
do_QueryInterface(xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)));
|
||||
if (!window || !window->GetDocShell() ||
|
||||
!window->GetDocShell()->GetRecordProfileTimelineMarkers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
|
||||
nsString filename;
|
||||
uint32_t lineNumber = 0;
|
||||
|
||||
js::AutoStableStringChars functionName(aCx);
|
||||
if (rootedFunction) {
|
||||
JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction));
|
||||
if (displayId) {
|
||||
functionName.initTwoByte(aCx, displayId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rootedScript) {
|
||||
rootedScript = JS_GetFunctionScript(aCx, rootedFunction);
|
||||
}
|
||||
if (rootedScript) {
|
||||
filename = NS_ConvertUTF8toUTF16(JS_GetScriptFilename(rootedScript));
|
||||
lineNumber = JS_GetScriptBaseLineNumber(aCx, rootedScript);
|
||||
}
|
||||
|
||||
if (!filename.IsEmpty() || functionName.isTwoByte()) {
|
||||
const char16_t* functionNameChars = functionName.isTwoByte() ?
|
||||
functionName.twoByteChars() : nullptr;
|
||||
|
||||
docShellForJSRunToCompletion->NotifyJSRunToCompletionStart(mReason,
|
||||
functionNameChars,
|
||||
filename.BeginReading(),
|
||||
lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AutoEntryScript::DocshellEntryMonitor::Exit(JSContext* aCx)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window =
|
||||
do_QueryInterface(xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)));
|
||||
// Not really worth checking GetRecordProfileTimelineMarkers here.
|
||||
if (window && window->GetDocShell()) {
|
||||
nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
|
||||
docShellForJSRunToCompletion->NotifyJSRunToCompletionStop();
|
||||
}
|
||||
}
|
||||
|
||||
AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
|
||||
: ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ false)
|
||||
, mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "js/Debug.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
class nsGlobalWindow;
|
||||
@ -339,6 +340,30 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// A subclass of AutoEntryMonitor that notifies the docshell.
|
||||
class DocshellEntryMonitor : public JS::dbg::AutoEntryMonitor
|
||||
{
|
||||
public:
|
||||
DocshellEntryMonitor(JSContext* aCx, const char* aReason);
|
||||
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction) override
|
||||
{
|
||||
Entry(aCx, aFunction, nullptr);
|
||||
}
|
||||
|
||||
void Entry(JSContext* aCx, JSScript* aScript) override
|
||||
{
|
||||
Entry(aCx, nullptr, aScript);
|
||||
}
|
||||
|
||||
void Exit(JSContext* aCx) override;
|
||||
|
||||
private:
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction, JSScript* aScript);
|
||||
|
||||
const char* mReason;
|
||||
};
|
||||
|
||||
// It's safe to make this a weak pointer, since it's the subject principal
|
||||
// when we go on the stack, so can't go away until after we're gone. In
|
||||
// particular, this is only used from the CallSetup constructor, and only in
|
||||
@ -349,7 +374,7 @@ private:
|
||||
nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal;
|
||||
friend nsIPrincipal* GetWebIDLCallerPrincipal();
|
||||
|
||||
nsCOMPtr<nsIDocShell> mDocShellForJSRunToCompletion;
|
||||
Maybe<DocshellEntryMonitor> mDocShellEntryMonitor;
|
||||
|
||||
bool mIsMainThread;
|
||||
};
|
||||
|
@ -1037,7 +1037,8 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual void AddDetails(mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
virtual void AddDetails(JSContext* aCx,
|
||||
mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
{
|
||||
if (GetMetaData() == TRACING_INTERVAL_START) {
|
||||
aMarker.mType.Construct(GetCause());
|
||||
|
@ -4,6 +4,17 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// For Javascript markers, the |stack| of a ProfileTimelineMarker
|
||||
// holds an object of this type. It intentionally looks like a
|
||||
// SavedStack object and is a representation of the frame that is
|
||||
// about to be constructed at the entry point.
|
||||
dictionary ProfileTimelineStackFrame {
|
||||
long line;
|
||||
long column = 0;
|
||||
DOMString source;
|
||||
DOMString functionDisplayName;
|
||||
};
|
||||
|
||||
dictionary ProfileTimelineLayerRect {
|
||||
long x = 0;
|
||||
long y = 0;
|
||||
|
@ -109,7 +109,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void AddDetails(mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
virtual void AddDetails(JSContext* aCx,
|
||||
mozilla::dom::ProfileTimelineMarker& aMarker) override
|
||||
{
|
||||
if (GetMetaData() == TRACING_INTERVAL_START) {
|
||||
aMarker.mRestyleHint.Construct(mRestyleHint);
|
||||
|
Loading…
Reference in New Issue
Block a user