зеркало из https://github.com/mozilla/gecko-dev.git
Merge the last PGO-green inbound changeset to m-c.
This commit is contained in:
Коммит
84aaf4a53c
13
configure.in
13
configure.in
|
@ -4223,7 +4223,7 @@ MOZ_WAVE=1
|
|||
MOZ_MEDIA=
|
||||
MOZ_OPUS=1
|
||||
MOZ_WEBM=1
|
||||
MOZ_WEBRTC=
|
||||
MOZ_WEBRTC=1
|
||||
MOZ_WEBRTC_SIGNALING=
|
||||
MOZ_MEDIA_PLUGINS=
|
||||
MOZ_MEDIA_NAVIGATOR=
|
||||
|
@ -4297,6 +4297,7 @@ case "${target}" in
|
|||
MOZ_TREE_FREETYPE=1
|
||||
MOZ_MEMORY=1
|
||||
MOZ_RAW=1
|
||||
MOZ_WEBRTC=
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -5248,12 +5249,12 @@ if test "$NS_PRINTING"; then
|
|||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable WebRTC code
|
||||
dnl = Disable WebRTC code
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(webrtc,
|
||||
[ --enable-webrtc Enable support for WebRTC],
|
||||
MOZ_WEBRTC=1,
|
||||
MOZ_WEBRTC=)
|
||||
MOZ_ARG_DISABLE_BOOL(webrtc,
|
||||
[ --disable-webrtc Disable support for WebRTC],
|
||||
MOZ_WEBRTC=,
|
||||
MOZ_WEBRTC=1)
|
||||
|
||||
if test -n "$MOZ_WEBRTC"; then
|
||||
AC_DEFINE(MOZ_WEBRTC)
|
||||
|
|
|
@ -1198,6 +1198,9 @@ nsContentSubtreeIterator::Init(nsIDOMRange* aRange)
|
|||
nsINode* endParent = mRange->GetEndParent();
|
||||
PRInt32 endOffset = mRange->EndOffset();
|
||||
MOZ_ASSERT(mCommonParent && startParent && endParent);
|
||||
// Bug 767169
|
||||
MOZ_ASSERT(startOffset <= startParent->Length() &&
|
||||
endOffset <= endParent->Length());
|
||||
|
||||
// short circuit when start node == end node
|
||||
if (startParent == endParent) {
|
||||
|
@ -1268,9 +1271,7 @@ nsContentSubtreeIterator::Init(nsIDOMRange* aRange)
|
|||
PRInt32 numChildren = endParent->GetChildCount();
|
||||
|
||||
if (offset > numChildren) {
|
||||
// Can happen for text nodes -- or if we're being called from
|
||||
// nsNodeUtils::ContentRemoved and the range hasn't been adjusted yet (bug
|
||||
// 767169).
|
||||
// Can happen for text nodes
|
||||
offset = numChildren;
|
||||
}
|
||||
if (!offset || !numChildren) {
|
||||
|
|
|
@ -699,10 +699,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
double cyp = -root * ry * x1p / rx;
|
||||
|
||||
double theta, delta;
|
||||
theta = AngleOfVector(gfxPoint((x1p-cxp)/rx, (y1p-cyp)/ry) - // F.6.5.5
|
||||
gfxPoint(1.0, 0.0));
|
||||
delta = AngleOfVector(gfxPoint((-x1p-cxp)/rx, (-y1p-cyp)/ry) - // F.6.5.6
|
||||
gfxPoint((x1p-cxp)/rx, (y1p-cyp)/ry));
|
||||
theta = AngleOfVector(gfxPoint((x1p-cxp)/rx, (y1p-cyp)/ry)); // F.6.5.5
|
||||
delta = AngleOfVector(gfxPoint((-x1p-cxp)/rx, (-y1p-cyp)/ry)) - // F.6.5.6
|
||||
theta;
|
||||
if (!sweepFlag && delta > 0)
|
||||
delta -= 2.0 * M_PI;
|
||||
else if (sweepFlag && delta < 0)
|
||||
|
|
|
@ -31,64 +31,81 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
let gTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
||||
/**
|
||||
* The window.console API implementation. One instance is lazily created for
|
||||
* every inner window, when the window.console object is accessed.
|
||||
*/
|
||||
function ConsoleAPI() {}
|
||||
ConsoleAPI.prototype = {
|
||||
|
||||
classID: Components.ID("{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver]),
|
||||
|
||||
_timerInitialized: false,
|
||||
_queuedCalls: null,
|
||||
_timerCallback: null,
|
||||
_destroyedWindows: null,
|
||||
_window: null,
|
||||
_innerID: null,
|
||||
_outerID: null,
|
||||
_windowDestroyed: false,
|
||||
_timer: null,
|
||||
|
||||
// nsIDOMGlobalPropertyInitializer
|
||||
init: function CA_init(aWindow) {
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this, "inner-window-destroyed", false);
|
||||
Services.obs.addObserver(this, "inner-window-destroyed", true);
|
||||
|
||||
try {
|
||||
let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
this._outerID = windowUtils.outerWindowID;
|
||||
this._innerID = windowUtils.currentInnerWindowID;
|
||||
}
|
||||
catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let chromeObject = {
|
||||
// window.console API
|
||||
log: function CA_log() {
|
||||
self.queueCall("log", arguments, aWindow);
|
||||
self.queueCall("log", arguments);
|
||||
},
|
||||
info: function CA_info() {
|
||||
self.queueCall("info", arguments, aWindow);
|
||||
self.queueCall("info", arguments);
|
||||
},
|
||||
warn: function CA_warn() {
|
||||
self.queueCall("warn", arguments, aWindow);
|
||||
self.queueCall("warn", arguments);
|
||||
},
|
||||
error: function CA_error() {
|
||||
self.queueCall("error", arguments, aWindow);
|
||||
self.queueCall("error", arguments);
|
||||
},
|
||||
debug: function CA_debug() {
|
||||
self.queueCall("debug", arguments, aWindow);
|
||||
self.queueCall("debug", arguments);
|
||||
},
|
||||
trace: function CA_trace() {
|
||||
self.queueCall("trace", arguments, aWindow);
|
||||
self.queueCall("trace", arguments);
|
||||
},
|
||||
// Displays an interactive listing of all the properties of an object.
|
||||
dir: function CA_dir() {
|
||||
self.queueCall("dir", arguments, aWindow);
|
||||
self.queueCall("dir", arguments);
|
||||
},
|
||||
group: function CA_group() {
|
||||
self.queueCall("group", arguments, aWindow);
|
||||
self.queueCall("group", arguments);
|
||||
},
|
||||
groupCollapsed: function CA_groupCollapsed() {
|
||||
self.queueCall("groupCollapsed", arguments, aWindow);
|
||||
self.queueCall("groupCollapsed", arguments);
|
||||
},
|
||||
groupEnd: function CA_groupEnd() {
|
||||
self.queueCall("groupEnd", arguments, aWindow);
|
||||
self.queueCall("groupEnd", arguments);
|
||||
},
|
||||
time: function CA_time() {
|
||||
self.queueCall("time", arguments, aWindow);
|
||||
self.queueCall("time", arguments);
|
||||
},
|
||||
timeEnd: function CA_timeEnd() {
|
||||
self.queueCall("timeEnd", arguments, aWindow);
|
||||
self.queueCall("timeEnd", arguments);
|
||||
},
|
||||
__exposedProps__: {
|
||||
log: "r",
|
||||
|
@ -135,24 +152,24 @@ ConsoleAPI.prototype = {
|
|||
Cu.makeObjectPropsNormal(contentObj);
|
||||
|
||||
this._queuedCalls = [];
|
||||
this._destroyedWindows = [];
|
||||
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._window = Cu.getWeakReference(aWindow);
|
||||
this.timerRegistry = {};
|
||||
|
||||
return contentObj;
|
||||
},
|
||||
|
||||
observe: function CA_observe(aSubject, aTopic, aData)
|
||||
{
|
||||
if (aTopic == "xpcom-shutdown") {
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||
this._destroyedWindows = [];
|
||||
this._queuedCalls = [];
|
||||
gTimer = null;
|
||||
}
|
||||
else if (aTopic == "inner-window-destroyed") {
|
||||
if (aTopic == "inner-window-destroyed") {
|
||||
let innerWindowID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
delete this.timerRegistry[innerWindowID + ""];
|
||||
this._destroyedWindows.push(innerWindowID);
|
||||
if (innerWindowID == this._innerID) {
|
||||
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||
this._windowDestroyed = true;
|
||||
if (!this._timerInitialized) {
|
||||
this.timerRegistry = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -163,29 +180,11 @@ ConsoleAPI.prototype = {
|
|||
* The console method the code has invoked.
|
||||
* @param object aArguments
|
||||
* The arguments passed to the console method.
|
||||
* @param object aWindow
|
||||
* The window from where the console method was called.
|
||||
*/
|
||||
queueCall: function CA_queueCall(aMethod, aArguments, aWindow)
|
||||
queueCall: function CA_queueCall(aMethod, aArguments)
|
||||
{
|
||||
let outerID;
|
||||
let innerID;
|
||||
try {
|
||||
let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
outerID = windowUtils.outerWindowID;
|
||||
innerID = windowUtils.currentInnerWindowID;
|
||||
}
|
||||
catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
let metaForCall = {
|
||||
outerID: outerID,
|
||||
innerID: innerID,
|
||||
isPrivate: PrivateBrowsingUtils.isWindowPrivate(aWindow),
|
||||
isPrivate: PrivateBrowsingUtils.isWindowPrivate(this._window.get()),
|
||||
timeStamp: Date.now(),
|
||||
stack: this.getStackTrace(aMethod != "trace" ? 1 : null),
|
||||
};
|
||||
|
@ -193,8 +192,8 @@ ConsoleAPI.prototype = {
|
|||
this._queuedCalls.push([aMethod, aArguments, metaForCall]);
|
||||
|
||||
if (!this._timerInitialized) {
|
||||
gTimer.initWithCallback(this._timerCallback.bind(this), CALL_DELAY,
|
||||
Ci.nsITimer.TYPE_REPEATING_SLACK);
|
||||
this._timer.initWithCallback(this._timerCallback.bind(this), CALL_DELAY,
|
||||
Ci.nsITimer.TYPE_REPEATING_SLACK);
|
||||
this._timerInitialized = true;
|
||||
}
|
||||
},
|
||||
|
@ -210,8 +209,12 @@ ConsoleAPI.prototype = {
|
|||
|
||||
if (!this._queuedCalls.length) {
|
||||
this._timerInitialized = false;
|
||||
this._destroyedWindows = [];
|
||||
gTimer.cancel();
|
||||
this._timer.cancel();
|
||||
|
||||
if (this._windowDestroyed) {
|
||||
ConsoleAPIStorage.clearEvents(this._innerID);
|
||||
this.timerRegistry = {};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -227,8 +230,6 @@ ConsoleAPI.prototype = {
|
|||
let [method, args, meta] = aCall;
|
||||
|
||||
let notifyMeta = {
|
||||
outerID: meta.outerID,
|
||||
innerID: meta.innerID,
|
||||
isPrivate: meta.isPrivate,
|
||||
timeStamp: meta.timeStamp,
|
||||
frame: meta.stack[0],
|
||||
|
@ -256,10 +257,10 @@ ConsoleAPI.prototype = {
|
|||
notifyArguments = args;
|
||||
break;
|
||||
case "time":
|
||||
notifyArguments = this.startTimer(meta.innerID, args[0], meta.timeStamp);
|
||||
notifyArguments = this.startTimer(args[0], meta.timeStamp);
|
||||
break;
|
||||
case "timeEnd":
|
||||
notifyArguments = this.stopTimer(meta.innerID, args[0], meta.timeStamp);
|
||||
notifyArguments = this.stopTimer(args[0], meta.timeStamp);
|
||||
break;
|
||||
default:
|
||||
// unknown console API method!
|
||||
|
@ -278,16 +279,14 @@ ConsoleAPI.prototype = {
|
|||
* The arguments given to the console API call.
|
||||
* @param object aMeta
|
||||
* Object that holds metadata about the console API call:
|
||||
* - outerID - the outer ID of the window where the message came from.
|
||||
* - innerID - the inner ID of the window where the message came from.
|
||||
* - isPrivate - Whether the window is in private browsing mode.
|
||||
* - frame - the youngest content frame in the call stack.
|
||||
* - timeStamp - when the console API call occurred.
|
||||
*/
|
||||
notifyObservers: function CA_notifyObservers(aLevel, aArguments, aMeta) {
|
||||
let consoleEvent = {
|
||||
ID: aMeta.outerID,
|
||||
innerID: aMeta.innerID,
|
||||
ID: this._outerID,
|
||||
innerID: this._innerID,
|
||||
level: aLevel,
|
||||
filename: aMeta.frame.filename,
|
||||
lineNumber: aMeta.frame.lineNumber,
|
||||
|
@ -299,12 +298,12 @@ ConsoleAPI.prototype = {
|
|||
consoleEvent.wrappedJSObject = consoleEvent;
|
||||
|
||||
// Store non-private messages for which the inner window was not destroyed.
|
||||
if (!aMeta.isPrivate && this._destroyedWindows.indexOf(aMeta.innerID) == -1) {
|
||||
ConsoleAPIStorage.recordEvent(aMeta.innerID, consoleEvent);
|
||||
if (!aMeta.isPrivate) {
|
||||
ConsoleAPIStorage.recordEvent(this._innerID, consoleEvent);
|
||||
}
|
||||
|
||||
Services.obs.notifyObservers(consoleEvent, "console-api-log-event",
|
||||
aMeta.outerID);
|
||||
this._outerID);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -384,19 +383,16 @@ ConsoleAPI.prototype = {
|
|||
},
|
||||
|
||||
/*
|
||||
* A registry of started timers. It contains a map of pages (defined by their
|
||||
* inner window IDs) to timer maps. Timer maps are key-value pairs of timer
|
||||
* names to timer start times, for all timers defined in that page. Timer
|
||||
* A registry of started timers. Timer maps are key-value pairs of timer
|
||||
* names to timer start times, for all timers defined in the page. Timer
|
||||
* names are prepended with the inner window ID in order to avoid conflicts
|
||||
* with Object.prototype functions.
|
||||
*/
|
||||
timerRegistry: {},
|
||||
timerRegistry: null,
|
||||
|
||||
/**
|
||||
* Create a new timer by recording the current time under the specified name.
|
||||
*
|
||||
* @param number aWindowId
|
||||
* The inner ID of the window.
|
||||
* @param string aName
|
||||
* The name of the timer.
|
||||
* @param number [aTimestamp=Date.now()]
|
||||
|
@ -407,30 +403,23 @@ ConsoleAPI.prototype = {
|
|||
* an object with the single property "error" that contains the key
|
||||
* for retrieving the localized error message.
|
||||
**/
|
||||
startTimer: function CA_startTimer(aWindowId, aName, aTimestamp) {
|
||||
startTimer: function CA_startTimer(aName, aTimestamp) {
|
||||
if (!aName) {
|
||||
return;
|
||||
}
|
||||
let innerID = aWindowId + "";
|
||||
if (!this.timerRegistry[innerID]) {
|
||||
this.timerRegistry[innerID] = {};
|
||||
}
|
||||
let pageTimers = this.timerRegistry[innerID];
|
||||
if (Object.keys(pageTimers).length > MAX_PAGE_TIMERS - 1) {
|
||||
if (Object.keys(this.timerRegistry).length > MAX_PAGE_TIMERS - 1) {
|
||||
return { error: "maxTimersExceeded" };
|
||||
}
|
||||
let key = aWindowId + "-" + aName.toString();
|
||||
if (!pageTimers[key]) {
|
||||
pageTimers[key] = aTimestamp || Date.now();
|
||||
let key = this._innerID + "-" + aName.toString();
|
||||
if (!(key in this.timerRegistry)) {
|
||||
this.timerRegistry[key] = aTimestamp || Date.now();
|
||||
}
|
||||
return { name: aName, started: pageTimers[key] };
|
||||
return { name: aName, started: this.timerRegistry[key] };
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop the timer with the specified name and retrieve the elapsed time.
|
||||
*
|
||||
* @param number aWindowId
|
||||
* The inner ID of the window.
|
||||
* @param string aName
|
||||
* The name of the timer.
|
||||
* @param number [aTimestamp=Date.now()]
|
||||
|
@ -439,21 +428,16 @@ ConsoleAPI.prototype = {
|
|||
* The name property holds the timer name and the duration property
|
||||
* holds the number of milliseconds since the timer was started.
|
||||
**/
|
||||
stopTimer: function CA_stopTimer(aWindowId, aName, aTimestamp) {
|
||||
stopTimer: function CA_stopTimer(aName, aTimestamp) {
|
||||
if (!aName) {
|
||||
return;
|
||||
}
|
||||
let innerID = aWindowId + "";
|
||||
let pageTimers = this.timerRegistry[innerID];
|
||||
if (!pageTimers) {
|
||||
let key = this._innerID + "-" + aName.toString();
|
||||
if (!(key in this.timerRegistry)) {
|
||||
return;
|
||||
}
|
||||
let key = aWindowId + "-" + aName.toString();
|
||||
if (!pageTimers[key]) {
|
||||
return;
|
||||
}
|
||||
let duration = (aTimestamp || Date.now()) - pageTimers[key];
|
||||
delete pageTimers[key];
|
||||
let duration = (aTimestamp || Date.now()) - this.timerRegistry[key];
|
||||
delete this.timerRegistry[key];
|
||||
return { name: aName, duration: duration };
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3967,6 +3967,14 @@ nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList,
|
|||
|
||||
*aResult = nsnull;
|
||||
|
||||
// We need this now to ensure that we have a non-null |presContext|
|
||||
// when we ought to.
|
||||
// This is similar to EnsureSizeUpToDate, but only flushes frames.
|
||||
nsGlobalWindow *parent = static_cast<nsGlobalWindow*>(GetPrivateParent());
|
||||
if (parent) {
|
||||
parent->FlushPendingNotifications(Flush_Frames);
|
||||
}
|
||||
|
||||
if (!mDocShell)
|
||||
return NS_OK;
|
||||
|
||||
|
|
|
@ -3270,7 +3270,8 @@ nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
|
|||
nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
|
||||
|
||||
if (ShouldReplaceRootElement()) {
|
||||
ResetRootElementAndEventTarget();
|
||||
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(
|
||||
this, &nsHTMLEditor::ResetRootElementAndEventTarget));
|
||||
}
|
||||
// We don't need to handle our own modifications
|
||||
else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) {
|
||||
|
@ -3300,7 +3301,8 @@ nsHTMLEditor::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
|
|||
nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
|
||||
|
||||
if (SameCOMIdentity(aChild, mRootElement)) {
|
||||
ResetRootElementAndEventTarget();
|
||||
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(
|
||||
this, &nsHTMLEditor::ResetRootElementAndEventTarget));
|
||||
}
|
||||
// We don't need to handle our own modifications
|
||||
else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) {
|
||||
|
|
|
@ -126,6 +126,7 @@ CPPSRCS = \
|
|||
ParseNode.cpp \
|
||||
Parser.cpp \
|
||||
SemanticAnalysis.cpp \
|
||||
SPSProfiler.cpp \
|
||||
TokenStream.cpp \
|
||||
TreeContext.cpp \
|
||||
TestingFunctions.cpp \
|
||||
|
|
|
@ -389,8 +389,6 @@ js::DirectEval(JSContext *cx, const CallArgs &args)
|
|||
JS_ASSERT(IsBuiltinEvalForScope(caller->scopeChain(), args.calleev()));
|
||||
JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
|
||||
|
||||
AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script());
|
||||
|
||||
if (!WarnOnTooManyArgs(cx, args))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ The meaning of the items:
|
|||
tz-pacific Always run test with the Pacific time zone (TZ=PST8PDT).
|
||||
mjitalways Run js with -a, whether --jitflags says to or not
|
||||
debug Run js with -d, whether --jitflags says to or not
|
||||
dump-bytecode Run js with -D, whether --jitflags says to or not
|
||||
|
||||
error The test should be considered to pass iff it throws the
|
||||
given JS exception.
|
||||
|
|
|
@ -111,6 +111,8 @@ class Test:
|
|||
test.jitflags.append('-d')
|
||||
elif name == 'mjit':
|
||||
test.jitflags.append('-m')
|
||||
elif name == 'dump-bytecode':
|
||||
test.jitflags.append('-D')
|
||||
else:
|
||||
print('warning: unrecognized |jit-test| attribute %s'%part)
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// |jit-test| mjit; mjitalways; dump-bytecode
|
||||
|
||||
function f() { }
|
||||
evaluate('function g() { f(); }', {newContext: true});
|
||||
for (var i = 0; i < 2; i++)
|
||||
g(0);
|
|
@ -63,6 +63,7 @@ CPPSRCS = \
|
|||
testValueABI.cpp \
|
||||
testVersion.cpp \
|
||||
testXDR.cpp \
|
||||
testProfileStrings.cpp \
|
||||
$(NULL)
|
||||
|
||||
CSRCS = \
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99:
|
||||
*
|
||||
* Tests the stack-based instrumentation profiler on a JSRuntime
|
||||
*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
static js::ProfileEntry stack[10];
|
||||
static uint32_t size = 0;
|
||||
static uint32_t max_stack = 0;
|
||||
|
||||
static void
|
||||
reset(JSContext *cx)
|
||||
{
|
||||
size = max_stack = 0;
|
||||
memset(stack, 0, sizeof(stack));
|
||||
cx->runtime->spsProfiler.stringsReset();
|
||||
}
|
||||
|
||||
static JSClass ptestClass = {
|
||||
"Prof", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
|
||||
};
|
||||
|
||||
static JSBool
|
||||
test_fn(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
max_stack = size;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
test_fn2(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
jsval r;
|
||||
return JS_CallFunctionName(cx, JS_GetGlobalObject(cx), "d", 0, NULL, &r);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
test_fn3(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 10);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Prof(JSContext* cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JSObject *obj = JS_NewObjectForConstructor(cx, &ptestClass, vp);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSFunctionSpec ptestFunctions[] = {
|
||||
JS_FS("test_fn", test_fn, 0, 0),
|
||||
JS_FS("test_fn2", test_fn2, 0, 0),
|
||||
JS_FS("test_fn3", test_fn3, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
initialize(JSContext *cx)
|
||||
{
|
||||
js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 10);
|
||||
return JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ptestClass, Prof, 0,
|
||||
NULL, ptestFunctions, NULL, NULL);
|
||||
}
|
||||
|
||||
BEGIN_TEST(testProfileStrings_isCalled)
|
||||
{
|
||||
CHECK(initialize(cx));
|
||||
|
||||
EXEC("function g() { var p = new Prof(); p.test_fn(); }");
|
||||
EXEC("function f() { g(); }");
|
||||
EXEC("function e() { f(); }");
|
||||
EXEC("function d() { e(); }");
|
||||
EXEC("function c() { d(); }");
|
||||
EXEC("function b() { c(); }");
|
||||
EXEC("function a() { b(); }");
|
||||
EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }");
|
||||
EXEC("function check2() { var p = new Prof(); p.test_fn2(); }");
|
||||
|
||||
reset(cx);
|
||||
{
|
||||
jsvalRoot rval(cx);
|
||||
/* Make sure the stack resets and we have an entry for each stack */
|
||||
CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
|
||||
CHECK(size == 0);
|
||||
CHECK(max_stack == 9);
|
||||
CHECK(cx->runtime->spsProfiler.stringsCount() == 8);
|
||||
/* Make sure the stack resets and we added no new entries */
|
||||
max_stack = 0;
|
||||
CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
|
||||
CHECK(size == 0);
|
||||
CHECK(max_stack == 9);
|
||||
CHECK(cx->runtime->spsProfiler.stringsCount() == 8);
|
||||
}
|
||||
reset(cx);
|
||||
{
|
||||
jsvalRoot rval(cx);
|
||||
CHECK(JS_CallFunctionName(cx, global, "check2", 0, NULL, rval.addr()));
|
||||
CHECK(cx->runtime->spsProfiler.stringsCount() == 5);
|
||||
CHECK(max_stack == 7);
|
||||
CHECK(size == 0);
|
||||
}
|
||||
reset(cx);
|
||||
{
|
||||
jsvalRoot rval(cx);
|
||||
js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 3);
|
||||
stack[3].string = (char*) 1234;
|
||||
CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
|
||||
CHECK((size_t) stack[3].string == 1234);
|
||||
CHECK(max_stack == 9);
|
||||
CHECK(size == 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
END_TEST(testProfileStrings_isCalled)
|
||||
|
||||
BEGIN_TEST(testProfileStrings_isCalledWithJIT)
|
||||
{
|
||||
CHECK(initialize(cx));
|
||||
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_METHODJIT |
|
||||
JSOPTION_METHODJIT_ALWAYS);
|
||||
|
||||
EXEC("function g() { var p = new Prof(); p.test_fn(); }");
|
||||
EXEC("function f() { g(); }");
|
||||
EXEC("function e() { f(); }");
|
||||
EXEC("function d() { e(); }");
|
||||
EXEC("function c() { d(); }");
|
||||
EXEC("function b() { c(); }");
|
||||
EXEC("function a() { b(); }");
|
||||
EXEC("function check() { var p = new Prof(); p.test_fn(); a(); }");
|
||||
EXEC("function check2() { var p = new Prof(); p.test_fn2(); }");
|
||||
|
||||
reset(cx);
|
||||
{
|
||||
jsvalRoot rval(cx);
|
||||
/* Make sure the stack resets and we have an entry for each stack */
|
||||
CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
|
||||
CHECK(size == 0);
|
||||
CHECK(max_stack == 9);
|
||||
|
||||
/* Make sure the stack resets and we added no new entries */
|
||||
uint32_t cnt = cx->runtime->spsProfiler.stringsCount();
|
||||
max_stack = 0;
|
||||
CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
|
||||
CHECK(size == 0);
|
||||
CHECK(cx->runtime->spsProfiler.stringsCount() == cnt);
|
||||
CHECK(max_stack == 9);
|
||||
}
|
||||
reset(cx);
|
||||
{
|
||||
/* Limit the size of the stack and make sure we don't overflow */
|
||||
jsvalRoot rval(cx);
|
||||
js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 3);
|
||||
stack[3].string = (char*) 1234;
|
||||
CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
|
||||
CHECK(size == 0);
|
||||
CHECK(max_stack == 9);
|
||||
CHECK((size_t) stack[3].string == 1234);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
END_TEST(testProfileStrings_isCalledWithJIT)
|
||||
|
||||
BEGIN_TEST(testProfileStrings_isCalledWhenError)
|
||||
{
|
||||
CHECK(initialize(cx));
|
||||
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_METHODJIT |
|
||||
JSOPTION_METHODJIT_ALWAYS);
|
||||
|
||||
EXEC("function check2() { throw 'a'; }");
|
||||
|
||||
reset(cx);
|
||||
{
|
||||
jsvalRoot rval(cx);
|
||||
/* Make sure the stack resets and we have an entry for each stack */
|
||||
JS_CallFunctionName(cx, global, "check2", 0, NULL, rval.addr());
|
||||
CHECK(size == 0);
|
||||
CHECK(cx->runtime->spsProfiler.stringsCount() == 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
END_TEST(testProfileStrings_isCalledWhenError)
|
||||
|
||||
BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
|
||||
{
|
||||
CHECK(initialize(cx));
|
||||
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_METHODJIT |
|
||||
JSOPTION_METHODJIT_ALWAYS);
|
||||
|
||||
EXEC("function b() { }");
|
||||
EXEC("function a() { var p = new Prof(); p.test_fn3(); b(); }");
|
||||
|
||||
reset(cx);
|
||||
js::SetRuntimeProfilingStack(cx->runtime, NULL, NULL, 10);
|
||||
{
|
||||
jsvalRoot rval(cx);
|
||||
JS_CallFunctionName(cx, global, "a", 0, NULL, rval.addr());
|
||||
CHECK(size == 0);
|
||||
CHECK(cx->runtime->spsProfiler.stringsCount() == 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
END_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
|
|
@ -30,6 +30,7 @@
|
|||
#include "js/HashTable.h"
|
||||
#include "js/Vector.h"
|
||||
#include "vm/Stack.h"
|
||||
#include "vm/SPSProfiler.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
@ -690,6 +691,9 @@ struct JSRuntime : js::RuntimeFriendFields
|
|||
/* If true, new compartments are initially in debug mode. */
|
||||
bool debugMode;
|
||||
|
||||
/* SPS profiling metadata */
|
||||
js::SPSProfiler spsProfiler;
|
||||
|
||||
/* If true, new scripts must be created with PC counter information. */
|
||||
bool profilingScripts;
|
||||
|
||||
|
|
1201
js/src/jsdate.cpp
1201
js/src/jsdate.cpp
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -867,4 +867,12 @@ GetTestingFunctions(JSContext *cx)
|
|||
return obj;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
|
||||
uint32_t max)
|
||||
{
|
||||
rt->spsProfiler.setProfilingStack(stack, size, max);
|
||||
ReleaseAllJITCode(rt->defaultFreeOp());
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -524,6 +524,31 @@ GetPCCountScriptSummary(JSContext *cx, size_t script);
|
|||
JS_FRIEND_API(JSString *)
|
||||
GetPCCountScriptContents(JSContext *cx, size_t script);
|
||||
|
||||
/*
|
||||
* A call stack can be specified to the JS engine such that all JS entry/exits
|
||||
* to functions push/pop an entry to/from the specified stack.
|
||||
*
|
||||
* For more detailed information, see vm/SPSProfiler.h
|
||||
*/
|
||||
struct ProfileEntry {
|
||||
/*
|
||||
* These two fields are marked as 'volatile' so that the compiler doesn't
|
||||
* re-order instructions which modify them. The operation in question is:
|
||||
*
|
||||
* stack[i].string = str;
|
||||
* (*size)++;
|
||||
*
|
||||
* If the size increment were re-ordered before the store of the string,
|
||||
* then if sampling occurred there would be a bogus entry on the stack.
|
||||
*/
|
||||
const char * volatile string;
|
||||
void * volatile sp;
|
||||
};
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
|
||||
uint32_t max);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_FRIEND_API(void *)
|
||||
GetOwnerThread(const JSContext *cx);
|
||||
|
|
|
@ -4681,7 +4681,7 @@ MaybeVerifyBarriers(JSContext *cx, bool always)
|
|||
|
||||
} /* namespace gc */
|
||||
|
||||
static void ReleaseAllJITCode(FreeOp *fop)
|
||||
void ReleaseAllJITCode(FreeOp *fop)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
for (CompartmentsIter c(fop->runtime()); !c.done(); c.next()) {
|
||||
|
|
|
@ -457,6 +457,9 @@ MaybeGC(JSContext *cx);
|
|||
extern void
|
||||
ShrinkGCBuffers(JSRuntime *rt);
|
||||
|
||||
extern void
|
||||
ReleaseAllJITCode(FreeOp *op);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
PrepareForFullGC(JSRuntime *rt);
|
||||
|
||||
|
|
|
@ -285,6 +285,8 @@ js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp)
|
|||
} check(cx);
|
||||
#endif
|
||||
|
||||
SPSEntryMarker marker(cx->runtime);
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
mjit::CompileStatus status;
|
||||
status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(),
|
||||
|
@ -1306,8 +1308,10 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
|||
* To support generator_throw and to catch ignored exceptions,
|
||||
* fail if cx->isExceptionPending() is true.
|
||||
*/
|
||||
if (cx->isExceptionPending())
|
||||
if (cx->isExceptionPending()) {
|
||||
Probes::enterScript(cx, script, script->function(), regs.fp());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1317,8 +1321,12 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
|||
/* Don't call the script prologue if executing between Method and Trace JIT. */
|
||||
if (interpMode == JSINTERP_NORMAL) {
|
||||
StackFrame *fp = regs.fp();
|
||||
if (!fp->isGeneratorFrame() && !fp->prologue(cx, UseNewTypeAtEntry(cx, fp)))
|
||||
goto error;
|
||||
if (!fp->isGeneratorFrame()) {
|
||||
if (!fp->prologue(cx, UseNewTypeAtEntry(cx, fp)))
|
||||
goto error;
|
||||
} else {
|
||||
Probes::enterScript(cx, script, script->function(), fp);
|
||||
}
|
||||
if (cx->compartment->debugMode()) {
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, fp);
|
||||
switch (status) {
|
||||
|
@ -1607,6 +1615,8 @@ BEGIN_CASE(JSOP_STOP)
|
|||
|
||||
if (!regs.fp()->isYielding())
|
||||
regs.fp()->epilogue(cx);
|
||||
else
|
||||
Probes::exitScript(cx, script, script->function(), regs.fp());
|
||||
|
||||
/* The JIT inlines the epilogue. */
|
||||
#ifdef JS_METHODJIT
|
||||
|
@ -2513,7 +2523,6 @@ BEGIN_CASE(JSOP_FUNCALL)
|
|||
|
||||
if (!regs.fp()->prologue(cx, newType))
|
||||
goto error;
|
||||
|
||||
if (cx->compartment->debugMode()) {
|
||||
switch (ScriptDebugPrologue(cx, regs.fp())) {
|
||||
case JSTRAP_CONTINUE:
|
||||
|
@ -3974,6 +3983,8 @@ END_CASE(JSOP_ARRAYPUSH)
|
|||
interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
|
||||
if (!regs.fp()->isYielding())
|
||||
regs.fp()->epilogue(cx);
|
||||
else
|
||||
Probes::exitScript(cx, script, script->function(), regs.fp());
|
||||
regs.fp()->setFinishedInInterpreter();
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
|
|
|
@ -92,10 +92,10 @@ bool callTrackingActive(JSContext *);
|
|||
bool wantNativeAddressInfo(JSContext *);
|
||||
|
||||
/* Entering a JS function */
|
||||
bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1);
|
||||
bool enterScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
|
||||
|
||||
/* About to leave a JS function */
|
||||
bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0);
|
||||
bool exitScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
|
||||
|
||||
/* Executing a script */
|
||||
bool startExecution(JSContext *cx, JSScript *script);
|
||||
|
@ -303,8 +303,8 @@ void
|
|||
discardExecutableRegion(void *start, size_t size);
|
||||
|
||||
/*
|
||||
* Internal: DTrace-specific functions to be called during Probes::enterJSFun
|
||||
* and Probes::exitJSFun. These will not be inlined, but the argument
|
||||
* Internal: DTrace-specific functions to be called during Probes::enterScript
|
||||
* and Probes::exitScript. These will not be inlined, but the argument
|
||||
* marshalling required for these probe points is expensive enough that it
|
||||
* shouldn't really matter.
|
||||
*/
|
||||
|
@ -380,43 +380,53 @@ Probes::wantNativeAddressInfo(JSContext *cx)
|
|||
}
|
||||
|
||||
inline bool
|
||||
Probes::enterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
|
||||
Probes::enterScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
|
||||
StackFrame *fp)
|
||||
{
|
||||
bool ok = true;
|
||||
#ifdef INCLUDE_MOZILLA_DTRACE
|
||||
if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
|
||||
DTraceEnterJSFun(cx, fun, script);
|
||||
DTraceEnterJSFun(cx, maybeFun, script);
|
||||
#endif
|
||||
#ifdef MOZ_TRACE_JSCALLS
|
||||
cx->doFunctionCallback(fun, script, counter);
|
||||
cx->doFunctionCallback(maybeFun, script, 1);
|
||||
#endif
|
||||
#ifdef MOZ_ETW
|
||||
if (ProfilingActive && !ETWEnterJSFun(cx, fun, script, counter))
|
||||
if (ProfilingActive && !ETWEnterJSFun(cx, maybeFun, script, 1))
|
||||
ok = false;
|
||||
#endif
|
||||
|
||||
JSRuntime *rt = cx->runtime;
|
||||
if (rt->spsProfiler.enabled()) {
|
||||
rt->spsProfiler.enter(cx, script, maybeFun);
|
||||
JS_ASSERT_IF(!fp->isGeneratorFrame(), !fp->hasPushedSPSFrame());
|
||||
fp->setPushedSPSFrame();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Probes::exitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
|
||||
Probes::exitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
|
||||
StackFrame *fp)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
#ifdef INCLUDE_MOZILLA_DTRACE
|
||||
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
|
||||
DTraceExitJSFun(cx, fun, script);
|
||||
DTraceExitJSFun(cx, maybeFun, script);
|
||||
#endif
|
||||
#ifdef MOZ_TRACE_JSCALLS
|
||||
if (counter > 0)
|
||||
counter = -counter;
|
||||
cx->doFunctionCallback(fun, script, counter);
|
||||
cx->doFunctionCallback(maybeFun, script, 0);
|
||||
#endif
|
||||
#ifdef MOZ_ETW
|
||||
if (ProfilingActive && !ETWExitJSFun(cx, fun, script, counter))
|
||||
if (ProfilingActive && !ETWExitJSFun(cx, maybeFun, script, 0))
|
||||
ok = false;
|
||||
#endif
|
||||
|
||||
JSRuntime *rt = cx->runtime;
|
||||
if (rt->spsProfiler.enabled() && fp->hasPushedSPSFrame())
|
||||
rt->spsProfiler.exit(cx, script, maybeFun);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -766,25 +776,6 @@ Probes::stopExecution(JSContext *cx, JSScript *script)
|
|||
return ok;
|
||||
}
|
||||
|
||||
struct AutoFunctionCallProbe {
|
||||
JSContext * const cx;
|
||||
JSFunction *fun;
|
||||
JSScript *script;
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
||||
AutoFunctionCallProbe(JSContext *cx, JSFunction *fun, JSScript *script
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: cx(cx), fun(fun), script(script)
|
||||
{
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
Probes::enterJSFun(cx, fun, script);
|
||||
}
|
||||
|
||||
~AutoFunctionCallProbe() {
|
||||
Probes::exitJSFun(cx, fun, script);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
|
|
|
@ -1416,6 +1416,7 @@ JSScript::finalize(FreeOp *fop)
|
|||
// fullyInitFromEmitter() or fullyInitTrivial().
|
||||
|
||||
CallDestroyScriptHook(fop, this);
|
||||
fop->runtime()->spsProfiler.onScriptFinalized(this);
|
||||
|
||||
JS_ASSERT_IF(principals, originPrincipals);
|
||||
if (principals)
|
||||
|
|
|
@ -282,6 +282,12 @@ mjit::Compiler::scanInlineCalls(uint32_t index, uint32_t depth)
|
|||
break;
|
||||
}
|
||||
|
||||
/* See bug 768313. */
|
||||
if (script->hasScriptCounts != outerScript->hasScriptCounts) {
|
||||
okay = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The outer and inner scripts must have the same scope. This only
|
||||
* allows us to inline calls between non-inner functions. Also
|
||||
|
@ -1174,17 +1180,11 @@ mjit::Compiler::generatePrologue()
|
|||
}
|
||||
}
|
||||
|
||||
if (debugMode()) {
|
||||
prepareStubCall(Uses(0));
|
||||
INLINE_STUBCALL(stubs::ScriptDebugPrologue, REJOIN_RESUME);
|
||||
} else if (Probes::callTrackingActive(cx)) {
|
||||
prepareStubCall(Uses(0));
|
||||
INLINE_STUBCALL(stubs::ScriptProbeOnlyPrologue, REJOIN_RESUME);
|
||||
}
|
||||
CompileStatus status = methodEntryHelper();
|
||||
if (status == Compile_Okay)
|
||||
recompileCheckHelper();
|
||||
|
||||
recompileCheckHelper();
|
||||
|
||||
return Compile_Okay;
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3740,7 +3740,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
|
|||
/* Only the top of the stack can be returned. */
|
||||
JS_ASSERT_IF(fe, fe == frame.peek(-1));
|
||||
|
||||
if (debugMode() || Probes::callTrackingActive(cx)) {
|
||||
if (debugMode()) {
|
||||
/* If the return value isn't in the frame's rval slot, move it there. */
|
||||
if (fe) {
|
||||
frame.storeTo(fe, Address(JSFrameReg, StackFrame::offsetOfReturnValue()), true);
|
||||
|
@ -3761,6 +3761,9 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
|
|||
}
|
||||
|
||||
if (a != outer) {
|
||||
JS_ASSERT(!debugMode());
|
||||
profilingPopHelper();
|
||||
|
||||
/*
|
||||
* Returning from an inlined script. The checks we do for inlineability
|
||||
* and recompilation triggered by args object construction ensure that
|
||||
|
@ -3800,8 +3803,12 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
|
|||
if (debugMode()) {
|
||||
prepareStubCall(Uses(0));
|
||||
INLINE_STUBCALL(stubs::Epilogue, REJOIN_NONE);
|
||||
} else if (script->function() && script->nesting()) {
|
||||
masm.sub32(Imm32(1), AbsoluteAddress(&script->nesting()->activeFrames));
|
||||
} else {
|
||||
profilingPopHelper();
|
||||
|
||||
if (script->function() && script->nesting()) {
|
||||
masm.sub32(Imm32(1), AbsoluteAddress(&script->nesting()->activeFrames));
|
||||
}
|
||||
}
|
||||
|
||||
emitReturnValue(&masm, fe);
|
||||
|
@ -3893,6 +3900,78 @@ mjit::Compiler::recompileCheckHelper()
|
|||
stubcc.rejoin(Changes(0));
|
||||
}
|
||||
|
||||
CompileStatus
|
||||
mjit::Compiler::methodEntryHelper()
|
||||
{
|
||||
if (debugMode()) {
|
||||
prepareStubCall(Uses(0));
|
||||
INLINE_STUBCALL(stubs::ScriptDebugPrologue, REJOIN_RESUME);
|
||||
} else if (Probes::callTrackingActive(cx)) {
|
||||
prepareStubCall(Uses(0));
|
||||
INLINE_STUBCALL(stubs::ScriptProbeOnlyPrologue, REJOIN_RESUME);
|
||||
} else {
|
||||
return profilingPushHelper();
|
||||
}
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
||||
CompileStatus
|
||||
mjit::Compiler::profilingPushHelper()
|
||||
{
|
||||
SPSProfiler *p = &cx->runtime->spsProfiler;
|
||||
if (!p->enabled())
|
||||
return Compile_Okay;
|
||||
/* If allocation fails, make sure no PopHelper() is emitted */
|
||||
const char *str = p->profileString(cx, script, script->function());
|
||||
if (str == NULL)
|
||||
return Compile_Error;
|
||||
|
||||
/* Check if there's still space on the stack */
|
||||
RegisterID size = frame.allocReg();
|
||||
RegisterID base = frame.allocReg();
|
||||
masm.load32(p->size(), size);
|
||||
Jump j = masm.branch32(Assembler::GreaterThanOrEqual, size,
|
||||
Imm32(p->maxSize()));
|
||||
|
||||
/* With room, store our string onto the stack */
|
||||
masm.move(ImmPtr(p->stack()), base);
|
||||
JS_STATIC_ASSERT(sizeof(ProfileEntry) == 2 * sizeof(void*));
|
||||
masm.lshift32(Imm32(sizeof(void*) == 4 ? 3 : 4), size);
|
||||
masm.addPtr(size, base);
|
||||
|
||||
masm.storePtr(ImmPtr(str), Address(base, offsetof(ProfileEntry, string)));
|
||||
masm.storePtr(ImmPtr(NULL), Address(base, offsetof(ProfileEntry, sp)));
|
||||
|
||||
frame.freeReg(base);
|
||||
frame.freeReg(size);
|
||||
|
||||
/* Always increment the stack size (paired with a decrement below) */
|
||||
j.linkTo(masm.label(), &masm);
|
||||
masm.add32(Imm32(1), AbsoluteAddress(p->size()));
|
||||
|
||||
/* Set the flags that we've pushed information onto the SPS stack */
|
||||
RegisterID reg = frame.allocReg();
|
||||
masm.load32(FrameFlagsAddress(), reg);
|
||||
masm.or32(Imm32(StackFrame::HAS_PUSHED_SPS_FRAME), reg);
|
||||
masm.store32(reg, FrameFlagsAddress());
|
||||
frame.freeReg(reg);
|
||||
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::profilingPopHelper()
|
||||
{
|
||||
if (Probes::callTrackingActive(cx) ||
|
||||
cx->runtime->spsProfiler.slowAssertionsEnabled())
|
||||
{
|
||||
prepareStubCall(Uses(0));
|
||||
INLINE_STUBCALL(stubs::ScriptProbeOnlyEpilogue, REJOIN_RESUME);
|
||||
} else if (cx->runtime->spsProfiler.enabled()) {
|
||||
masm.sub32(Imm32(1), AbsoluteAddress(cx->runtime->spsProfiler.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::addReturnSite()
|
||||
{
|
||||
|
@ -4456,7 +4535,10 @@ mjit::Compiler::inlineScriptedFunction(uint32_t argc, bool callingNew)
|
|||
|
||||
markUndefinedLocals();
|
||||
|
||||
status = generateMethod();
|
||||
status = methodEntryHelper();
|
||||
if (status == Compile_Okay)
|
||||
status = generateMethod();
|
||||
|
||||
if (status != Compile_Okay) {
|
||||
popActiveFrame();
|
||||
if (status == Compile_Abort) {
|
||||
|
|
|
@ -637,6 +637,9 @@ private:
|
|||
void dispatchCall(VoidPtrStubUInt32 stub, uint32_t argc);
|
||||
void interruptCheckHelper();
|
||||
void recompileCheckHelper();
|
||||
CompileStatus methodEntryHelper();
|
||||
CompileStatus profilingPushHelper();
|
||||
void profilingPopHelper();
|
||||
void emitUncachedCall(uint32_t argc, bool callingNew);
|
||||
void checkCallApplySpeculation(uint32_t argc, FrameEntry *origCallee, FrameEntry *origThis,
|
||||
MaybeRegisterID origCalleeType, RegisterID origCalleeData,
|
||||
|
|
|
@ -610,7 +610,7 @@ stubs::CreateThis(VMFrame &f, JSObject *proto)
|
|||
void JS_FASTCALL
|
||||
stubs::ScriptDebugPrologue(VMFrame &f)
|
||||
{
|
||||
Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
|
||||
Probes::enterScript(f.cx, f.script(), f.script()->function(), f.fp());
|
||||
JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
|
@ -629,7 +629,6 @@ stubs::ScriptDebugPrologue(VMFrame &f)
|
|||
void JS_FASTCALL
|
||||
stubs::ScriptDebugEpilogue(VMFrame &f)
|
||||
{
|
||||
Probes::exitJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
|
||||
if (!js::ScriptDebugEpilogue(f.cx, f.fp(), JS_TRUE))
|
||||
THROW();
|
||||
}
|
||||
|
@ -637,13 +636,13 @@ stubs::ScriptDebugEpilogue(VMFrame &f)
|
|||
void JS_FASTCALL
|
||||
stubs::ScriptProbeOnlyPrologue(VMFrame &f)
|
||||
{
|
||||
Probes::enterJSFun(f.cx, f.fp()->fun(), f.fp()->script());
|
||||
Probes::enterScript(f.cx, f.script(), f.script()->function(), f.fp());
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::ScriptProbeOnlyEpilogue(VMFrame &f)
|
||||
{
|
||||
Probes::exitJSFun(f.cx, f.fp()->fun(), f.fp()->script());
|
||||
Probes::exitScript(f.cx, f.script(), f.script()->function(), f.fp());
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
|
@ -868,8 +867,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
|
|||
return js_InternalThrow(f);
|
||||
fp->thisValue() = ObjectValue(*obj);
|
||||
|
||||
if (Probes::callTrackingActive(cx))
|
||||
Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
|
||||
Probes::enterScript(f.cx, f.script(), f.script()->function(), fp);
|
||||
|
||||
if (script->debugMode) {
|
||||
JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
|
||||
|
@ -925,8 +923,8 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
|
|||
}
|
||||
/* FALLTHROUGH */
|
||||
case REJOIN_EVAL_PROLOGUE:
|
||||
Probes::enterScript(cx, f.script(), f.script()->function(), fp);
|
||||
if (cx->compartment->debugMode()) {
|
||||
Probes::enterJSFun(cx, fp->maybeFun(), fp->script());
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, fp);
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
|
|
|
@ -3410,6 +3410,29 @@ EnableStackWalkingAssertion(JSContext *cx, unsigned argc, jsval *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
EnableSPSProfilingAssertions(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
jsval arg = JS_ARGV(cx, vp)[0];
|
||||
if (argc == 0 || !JSVAL_IS_BOOLEAN(arg)) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS,
|
||||
"enableSPSProfilingAssertions");
|
||||
return false;
|
||||
}
|
||||
|
||||
static ProfileEntry stack[1000];
|
||||
static uint32_t stack_size = 0;
|
||||
|
||||
if (JSVAL_TO_BOOLEAN(arg))
|
||||
SetRuntimeProfilingStack(cx->runtime, stack, &stack_size, 1000);
|
||||
else
|
||||
SetRuntimeProfilingStack(cx->runtime, NULL, NULL, 0);
|
||||
cx->runtime->spsProfiler.enableSlowAssertions(JSVAL_TO_BOOLEAN(arg));
|
||||
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp)
|
||||
{
|
||||
|
@ -3710,6 +3733,11 @@ static JSFunctionSpecWithHelp shell_functions[] = {
|
|||
"getMaxArgs()",
|
||||
" Return the maximum number of supported args for a call."),
|
||||
|
||||
JS_FN_HELP("enableSPSProfilingAssertions", EnableSPSProfilingAssertions, 1, 0,
|
||||
"enableProfilingAssertions(enabled)",
|
||||
" Enables or disables the assertions related to SPS profiling. This is fairly\n"
|
||||
" expensive, so it shouldn't be enabled normally."),
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
#ifdef MOZ_PROFILING
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "vm/SPSProfiler.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
SPSProfiler::~SPSProfiler()
|
||||
{
|
||||
if (strings.initialized()) {
|
||||
for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront())
|
||||
js_free((void*) e.front().value);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SPSProfiler::setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max)
|
||||
{
|
||||
if (!strings.initialized())
|
||||
strings.init(max);
|
||||
stack_ = stack;
|
||||
size_ = size;
|
||||
max_ = max;
|
||||
}
|
||||
|
||||
/* Lookup the string for the function/script, creating one if necessary */
|
||||
const char*
|
||||
SPSProfiler::profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun)
|
||||
{
|
||||
JS_ASSERT(enabled());
|
||||
JS_ASSERT(strings.initialized());
|
||||
ProfileStringMap::AddPtr s = strings.lookupForAdd(script);
|
||||
if (s)
|
||||
return s->value;
|
||||
const char *str = allocProfileString(cx, script, maybeFun);
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
if (!strings.add(s, script, str)) {
|
||||
js_free((void*) str);
|
||||
return NULL;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void
|
||||
SPSProfiler::onScriptFinalized(JSScript *script)
|
||||
{
|
||||
/*
|
||||
* This function is called whenever a script is destroyed, regardless of
|
||||
* whether profiling has been turned on, so don't invoke a function on an
|
||||
* invalid hash set. Also, even if profiling was enabled but then turned
|
||||
* off, we still want to remove the string, so no check of enabled() is
|
||||
* done.
|
||||
*/
|
||||
if (!strings.initialized())
|
||||
return;
|
||||
if (ProfileStringMap::Ptr entry = strings.lookup(script)) {
|
||||
const char *tofree = entry->value;
|
||||
strings.remove(entry);
|
||||
js_free((void*) tofree);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SPSProfiler::enter(JSContext *cx, JSScript *script, JSFunction *maybeFun)
|
||||
{
|
||||
JS_ASSERT(enabled());
|
||||
const char *str = profileString(cx, script, maybeFun);
|
||||
if (str == NULL)
|
||||
return false;
|
||||
|
||||
if (*size_ < max_) {
|
||||
stack_[*size_].string = str;
|
||||
stack_[*size_].sp = NULL;
|
||||
}
|
||||
(*size_)++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SPSProfiler::exit(JSContext *cx, JSScript *script, JSFunction *maybeFun)
|
||||
{
|
||||
JS_ASSERT(enabled());
|
||||
(*size_)--;
|
||||
JS_ASSERT(*(int*)size_ >= 0);
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Sanity check to make sure push/pop balanced */
|
||||
if (*size_ < max_) {
|
||||
const char *str = profileString(cx, script, maybeFun);
|
||||
/* Can't fail lookup because we should already be in the set */
|
||||
JS_ASSERT(str != NULL);
|
||||
JS_ASSERT(strcmp((const char*) stack_[*size_].string, str) == 0);
|
||||
stack_[*size_].string = NULL;
|
||||
stack_[*size_].sp = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Serializes the script/function pair into a "descriptive string" which is
|
||||
* allowed to fail. This function cannot trigger a GC because it could finalize
|
||||
* some scripts, resize the hash table of profile strings, and invalidate the
|
||||
* AddPtr held while invoking allocProfileString.
|
||||
*/
|
||||
const char*
|
||||
SPSProfiler::allocProfileString(JSContext *cx, JSScript *script, JSFunction *maybeFun)
|
||||
{
|
||||
DebugOnly<uint64_t> gcBefore = cx->runtime->gcNumber;
|
||||
StringBuffer buf(cx);
|
||||
bool hasAtom = maybeFun != NULL && maybeFun->atom != NULL;
|
||||
if (hasAtom) {
|
||||
if (!buf.append(maybeFun->atom))
|
||||
return NULL;
|
||||
if (!buf.append(" ("))
|
||||
return NULL;
|
||||
}
|
||||
if (script->filename) {
|
||||
if (!buf.appendInflated(script->filename, strlen(script->filename)))
|
||||
return NULL;
|
||||
} else if (!buf.append("<unknown>")) {
|
||||
return NULL;
|
||||
}
|
||||
if (!buf.append(":"))
|
||||
return NULL;
|
||||
if (!NumberValueToStringBuffer(cx, NumberValue(script->lineno), buf))
|
||||
return NULL;
|
||||
if (hasAtom && !buf.append(")"))
|
||||
return NULL;
|
||||
|
||||
size_t len = buf.length();
|
||||
char *cstr = (char*) js_malloc(len + 1);
|
||||
if (cstr == NULL)
|
||||
return NULL;
|
||||
|
||||
const jschar *ptr = buf.begin();
|
||||
for (size_t i = 0; i < len; i++)
|
||||
cstr[i] = ptr[i];
|
||||
cstr[len] = 0;
|
||||
|
||||
JS_ASSERT(gcBefore == cx->runtime->gcNumber);
|
||||
return cstr;
|
||||
}
|
||||
|
||||
SPSEntryMarker::SPSEntryMarker(JSRuntime *rt) : profiler(&rt->spsProfiler), pushed(false)
|
||||
{
|
||||
if (!profiler->enabled())
|
||||
return;
|
||||
uint32_t *size = profiler->size_;
|
||||
size_before = *size;
|
||||
if (*size < profiler->max_) {
|
||||
profiler->stack_[*size].string = "js::RunScript";
|
||||
profiler->stack_[*size].sp = this;
|
||||
}
|
||||
(*size)++;
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
SPSEntryMarker::~SPSEntryMarker()
|
||||
{
|
||||
if (!pushed || !profiler->enabled())
|
||||
return;
|
||||
(*profiler->size_)--;
|
||||
JS_ASSERT(*profiler->size_ == size_before);
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SPSProfiler_h__
|
||||
#define SPSProfiler_h__
|
||||
|
||||
#include "mozilla/HashFunctions.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsscript.h"
|
||||
|
||||
/*
|
||||
* SPS Profiler integration with the JS Engine
|
||||
* https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler
|
||||
*
|
||||
* The SPS profiler (found in tools/profiler) is an implementation of a profiler
|
||||
* which has the ability to walk the C++ stack as well as use instrumentation to
|
||||
* gather information. When dealing with JS, however, SPS needs integration
|
||||
* with the engine because otherwise it is very difficult to figure out what
|
||||
* javascript is executing.
|
||||
*
|
||||
* The current method of integration with SPS is a form of instrumentation:
|
||||
* every time a JS function is entered, a bit of information is pushed onto a
|
||||
* stack that SPS owns and maintains. This information is then popped at the end
|
||||
* of the JS function. SPS informs the JS engine of this stack at runtime, and
|
||||
* it can by turned on/off dynamically.
|
||||
*
|
||||
* The SPS stack has three parameters: a base pointer, a size, and a maximum
|
||||
* size. The stack is the ProfileEntry stack which will have information written
|
||||
* to it. The size location is a pointer to an integer which represents the
|
||||
* current size of the stack (number of valid frames). This size will be
|
||||
* modified when JS functions are called. The maximum specified is the maximum
|
||||
* capacity of the ProfileEntry stack.
|
||||
*
|
||||
* Throughout execution, the size of the stack recorded in memory may exceed the
|
||||
* maximum. The JS engine will not write any information past the maximum limit,
|
||||
* but it will still maintain the size of the stack. SPS code is aware of this
|
||||
* and iterates the stack accordingly.
|
||||
*
|
||||
* There are two pointers of information pushed on the SPS stack for every JS
|
||||
* function that is entered. First is a char* pointer of a description of what
|
||||
* function was entered. Currently this string is of the form
|
||||
* "function (file:line)" if there's a function name, or just "file:line" if
|
||||
* there's no function name available. The other bit of information is the
|
||||
* relevant C++ (native) stack pointer. This stack pointer is what enables the
|
||||
* interleaving of the C++ and the JS stack.
|
||||
*
|
||||
* = Profile Strings
|
||||
*
|
||||
* The profile strings' allocations and deallocation must be carefully
|
||||
* maintained, and ideally at a very low overhead cost. For this reason, the JS
|
||||
* engine maintains a mapping of all known profile strings. These strings are
|
||||
* keyed in lookup by a JSScript*, but are serialized with a JSFunction*,
|
||||
* JSScript* pair. A JSScript will destroy its corresponding profile string when
|
||||
* the script is finalized.
|
||||
*
|
||||
* For this reason, a char* pointer pushed on the SPS stack is valid only while
|
||||
* it is on the SPS stack. SPS uses sampling to read off information from this
|
||||
* instrumented stack, and it therefore copies the string byte for byte when a
|
||||
* JS function is encountered during sampling.
|
||||
*
|
||||
* = Native Stack Pointer
|
||||
*
|
||||
* The actual value pushed as the native pointer is NULL for most JS functions.
|
||||
* The reason for this is that there's actually very little correlation between
|
||||
* the JS stack and the C++ stack because many JS functions all run in the same
|
||||
* C++ frame, or can even go backwards in C++ when going from the JIT back to
|
||||
* the interpreter.
|
||||
*
|
||||
* To alleviate this problem, all JS functions push NULL as their "native stack
|
||||
* pointer" to indicate that it's a JS function call. The function RunScript(),
|
||||
* however, pushes an actual C++ stack pointer onto the SPS stack. This way when
|
||||
* interleaving C++ and JS, if SPS sees a NULL native stack pointer on the SPS
|
||||
* stack, it looks backwards for the first non-NULL pointer and uses that for
|
||||
* all subsequent NULL native stack pointers.
|
||||
*/
|
||||
|
||||
namespace js {
|
||||
|
||||
typedef HashMap<JSScript*, const char*, DefaultHasher<JSScript*>, SystemAllocPolicy>
|
||||
ProfileStringMap;
|
||||
|
||||
class SPSEntryMarker;
|
||||
|
||||
class SPSProfiler
|
||||
{
|
||||
friend class SPSEntryMarker;
|
||||
|
||||
ProfileStringMap strings;
|
||||
ProfileEntry *stack_;
|
||||
uint32_t *size_;
|
||||
uint32_t max_;
|
||||
bool slowAssertions;
|
||||
|
||||
static const char *allocProfileString(JSContext *cx, JSScript *script,
|
||||
JSFunction *function);
|
||||
public:
|
||||
SPSProfiler() : stack_(NULL), size_(NULL), max_(0), slowAssertions(false) {}
|
||||
~SPSProfiler();
|
||||
|
||||
uint32_t *size() { return size_; }
|
||||
uint32_t maxSize() { return max_; }
|
||||
ProfileEntry *stack() { return stack_; }
|
||||
|
||||
bool enabled() { return stack_ != NULL; }
|
||||
void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
|
||||
bool slowAssertionsEnabled() { return slowAssertions; }
|
||||
bool enter(JSContext *cx, JSScript *script, JSFunction *maybeFun);
|
||||
void exit(JSContext *cx, JSScript *script, JSFunction *maybeFun);
|
||||
void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
|
||||
const char *profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun);
|
||||
void onScriptFinalized(JSScript *script);
|
||||
|
||||
/* meant to be used for testing, not recommended to call in normal code */
|
||||
size_t stringsCount() { return strings.count(); }
|
||||
void stringsReset() { strings.clear(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* This class is used in RunScript() to push the marker onto the sampling stack
|
||||
* that we're about to enter JS function calls. This is the only time in which a
|
||||
* valid stack pointer is pushed to the sampling stack.
|
||||
*/
|
||||
class SPSEntryMarker
|
||||
{
|
||||
SPSProfiler *profiler;
|
||||
bool pushed;
|
||||
DebugOnly<uint32_t> size_before;
|
||||
|
||||
public:
|
||||
SPSEntryMarker(JSRuntime *rt);
|
||||
~SPSEntryMarker();
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* SPSProfiler_h__ */
|
|
@ -114,6 +114,9 @@ StackFrame::initInlineFrame(JSFunction *fun, StackFrame *prevfp, jsbytecode *pre
|
|||
flags_ = StackFrame::FUNCTION;
|
||||
exec.fun = fun;
|
||||
resetInlinePrev(prevfp, prevpc);
|
||||
|
||||
if (prevfp->hasPushedSPSFrame())
|
||||
setPushedSPSFrame();
|
||||
}
|
||||
|
||||
inline void
|
||||
|
|
|
@ -237,11 +237,14 @@ StackFrame::prologue(JSContext *cx, bool newType)
|
|||
pushOnScopeChain(*callobj);
|
||||
flags_ |= HAS_CALL_OBJ;
|
||||
}
|
||||
Probes::enterScript(cx, script(), NULL, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isGlobalFrame())
|
||||
if (isGlobalFrame()) {
|
||||
Probes::enterScript(cx, script(), NULL, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(isNonEvalFunctionFrame());
|
||||
|
||||
|
@ -266,7 +269,7 @@ StackFrame::prologue(JSContext *cx, bool newType)
|
|||
functionThis() = ObjectValue(*obj);
|
||||
}
|
||||
|
||||
Probes::enterJSFun(cx, fun(), script());
|
||||
Probes::enterScript(cx, script(), script()->function(), this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -277,6 +280,8 @@ StackFrame::epilogue(JSContext *cx)
|
|||
JS_ASSERT(!isYielding());
|
||||
JS_ASSERT(!hasBlockChain());
|
||||
|
||||
Probes::exitScript(cx, script(), script()->function(), this);
|
||||
|
||||
if (isEvalFrame()) {
|
||||
if (isStrictEvalFrame()) {
|
||||
JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().isForEval());
|
||||
|
@ -309,8 +314,6 @@ StackFrame::epilogue(JSContext *cx)
|
|||
if (cx->compartment->debugMode())
|
||||
cx->runtime->debugScopes->onPopCall(this, cx);
|
||||
|
||||
Probes::exitJSFun(cx, fun(), script());
|
||||
|
||||
if (script()->nesting() && (flags_ & HAS_NESTING))
|
||||
types::NestingEpilogue(this);
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
struct JSContext;
|
||||
struct JSCompartment;
|
||||
|
||||
extern void js_DumpStackFrame(JSContext *, js::StackFrame *);
|
||||
|
||||
namespace js {
|
||||
|
||||
class StackFrame;
|
||||
|
@ -57,10 +55,6 @@ struct InlinedSite {};
|
|||
#endif
|
||||
typedef size_t FrameRejoinState;
|
||||
|
||||
namespace detail {
|
||||
struct OOMCheck;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -266,7 +260,10 @@ class StackFrame
|
|||
LOWERED_CALL_APPLY = 0x200000, /* Pushed by a lowered call/apply */
|
||||
|
||||
/* Debugger state */
|
||||
PREV_UP_TO_DATE = 0x400000 /* see DebugScopes::updateLiveScopes */
|
||||
PREV_UP_TO_DATE = 0x400000, /* see DebugScopes::updateLiveScopes */
|
||||
|
||||
/* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */
|
||||
HAS_PUSHED_SPS_FRAME = 0x800000 /* SPS was notified of enty */
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -801,6 +798,14 @@ class StackFrame
|
|||
flags_ |= HAS_HOOK_DATA;
|
||||
}
|
||||
|
||||
bool hasPushedSPSFrame() {
|
||||
return !!(flags_ & HAS_PUSHED_SPS_FRAME);
|
||||
}
|
||||
|
||||
void setPushedSPSFrame() {
|
||||
flags_ |= HAS_PUSHED_SPS_FRAME;
|
||||
}
|
||||
|
||||
/* Return value */
|
||||
|
||||
bool hasReturnValue() const {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "sampler.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
@ -2000,6 +2001,10 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
|
||||
#endif
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (ProfileStack *stack = mozilla_profile_stack())
|
||||
stack->sampleRuntime(mJSRuntime);
|
||||
#endif
|
||||
JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
|
||||
js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Reference for test that marker orientation is correct at the end of arcs</title>
|
||||
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=769115 -->
|
||||
|
||||
<defs>
|
||||
<g id="m1" transform="translate(-20,-20)" fill="blue">
|
||||
<rect x="5" y="15" width="22" height="10"/>
|
||||
<path d="M 25,10 35,20 25,30 z"/>
|
||||
</g>
|
||||
|
||||
<g id="m2" transform="translate(-20,-20)" fill="red">
|
||||
<rect x="5" y="15" width="22" height="10"/>
|
||||
<path d="M 25,10 35,20 25,30 z"/>
|
||||
</g>
|
||||
</defs>
|
||||
|
||||
<g fill="none">
|
||||
<!-- arcs that go from the left of the circle to... -->
|
||||
<g>
|
||||
<!-- ...90 degrees anti-clockwise -->
|
||||
<use xlink:href="#m1" transform="translate(100,100)rotate(90)"/>
|
||||
<use xlink:href="#m2" transform="rotate(-90,150,100)translate(100,100)rotate(90)"/>
|
||||
<!-- ...180 degrees anti-clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(-180,150,100)translate(100,100)rotate(90)"/>
|
||||
<!-- ...270 degrees anti-clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(-270,150,100)translate(100,100)rotate(90)"/>
|
||||
</g>
|
||||
|
||||
<!-- arcs that go from the left of the circle to... -->
|
||||
<g transform="translate(250,0)">
|
||||
<!-- ...90 degrees anti-clockwise -->
|
||||
<use xlink:href="#m1" transform="translate(100,100)rotate(-90)"/>
|
||||
<use xlink:href="#m2" transform="rotate(90,150,100)translate(100,100)rotate(-90)"/>
|
||||
<!-- ...180 degrees anti-clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(180,150,100)translate(100,100)rotate(-90)"/>
|
||||
<!-- ...270 degrees anti-clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(270,150,100)translate(100,100)rotate(-90)"/>
|
||||
</g>
|
||||
|
||||
<!-- arcs that go from the right of the circle to... -->
|
||||
<g transform="translate(0,250)">
|
||||
<!-- ...90 degrees anti-clockwise -->
|
||||
<use xlink:href="#m1" transform="translate(200,100)rotate(90)"/>
|
||||
<use xlink:href="#m2" transform="rotate(90,150,100)translate(200,100)rotate(90)"/>
|
||||
<!-- ...180 degrees anti-clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(180,150,100)translate(200,100)rotate(90)"/>
|
||||
<!-- ...270 degrees anti-clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(270,150,100)translate(200,100)rotate(90)"/>
|
||||
</g>
|
||||
|
||||
<!-- arcs that go from the right of the circle to... -->
|
||||
<g transform="translate(250,250)">
|
||||
<!-- ...90 degrees clockwise -->
|
||||
<use xlink:href="#m1" transform="translate(200,100)rotate(-90)"/>
|
||||
<use xlink:href="#m2" transform="rotate(-90,150,100)translate(200,100)rotate(-90)"/>
|
||||
<!-- ...180 degrees clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(-180,150,100)translate(200,100)rotate(-90)"/>
|
||||
<!-- ...270 degrees clockwise -->
|
||||
<use xlink:href="#m2" transform="rotate(-270,150,100)translate(200,100)rotate(-90)"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 3.0 KiB |
|
@ -0,0 +1,63 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Test that marker orientation is correct at the end of arcs</title>
|
||||
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=769115 -->
|
||||
|
||||
<marker id="m1" markerWidth="40" markerHeight="40" refX="20" refY="20"
|
||||
markerUnits="userSpaceOnUse" orient="auto" fill="blue">
|
||||
<rect x="5" y="15" width="22" height="10"/>
|
||||
<path d="M 25,10 35,20 25,30 z"/>
|
||||
</marker>
|
||||
|
||||
<marker id="m2" markerWidth="40" markerHeight="40" refX="20" refY="20"
|
||||
markerUnits="userSpaceOnUse" orient="auto" fill="red">
|
||||
<rect x="5" y="15" width="22" height="10"/>
|
||||
<path d="M 25,10 35,20 25,30 z"/>
|
||||
</marker>
|
||||
|
||||
<g fill="none">
|
||||
<!-- arcs that go from the left of the circle to... -->
|
||||
<g marker-end="url(#m2)">
|
||||
<!-- ...90 degrees anti-clockwise -->
|
||||
<path d="M100,100 A 50,50 0 1 0 150,50" marker-start="url(#m1)"/>
|
||||
<!-- ...180 degrees anti-clockwise -->
|
||||
<path d="M100,100 A 50,50 0 0 0 200,100"/>
|
||||
<!-- ...270 degrees anti-clockwise -->
|
||||
<path d="M100,100 A 50,50 0 0 0 150,150"/>
|
||||
</g>
|
||||
|
||||
<!-- arcs that go from the left of the circle to... -->
|
||||
<g marker-end="url(#m2)" transform="translate(250,0)">
|
||||
<!-- ...90 degrees clockwise -->
|
||||
<path d="M100,100 A 50,50 0 0 1 150,50" marker-start="url(#m1)"/>
|
||||
<!-- ...180 degrees clockwise -->
|
||||
<path d="M100,100 A 50,50 0 1 1 200,100"/>
|
||||
<!-- ...270 degrees clockwise -->
|
||||
<path d="M100,100 A 50,50 0 1 1 150,150"/>
|
||||
</g>
|
||||
|
||||
<!-- arcs that go from the right of the circle to... -->
|
||||
<g marker-end="url(#m2)" transform="translate(0,250)">
|
||||
<!-- ...90 degrees anti-clockwise -->
|
||||
<path d="M200,100 A 50,50 0 0 1 150,150" marker-start="url(#m1)"/>
|
||||
<!-- ...180 degrees anti-clockwise -->
|
||||
<path d="M200,100 A 50,50 0 0 1 100,100"/>
|
||||
<!-- ...270 degrees anti-clockwise -->
|
||||
<path d="M200,100 A 50,50 0 1 1 150,50"/>
|
||||
</g>
|
||||
|
||||
<!-- arcs that go from the right of the circle to... -->
|
||||
<g marker-end="url(#m2)" transform="translate(250,250)">
|
||||
<!-- ...90 degrees anti-clockwise -->
|
||||
<path d="M200,100 A 50,50 0 0 0 150,50" marker-start="url(#m1)"/>
|
||||
<!-- ...180 degrees anti-clockwise -->
|
||||
<path d="M200,100 A 50,50 0 1 0 100,100"/>
|
||||
<!-- ...270 degrees anti-clockwise -->
|
||||
<path d="M200,100 A 50,50 0 1 0 150,150"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 2.4 KiB |
|
@ -169,6 +169,7 @@ fails == inline-in-xul-basic-01.xul pass.svg
|
|||
== markers-and-group-opacity-01.svg markers-and-group-opacity-01-ref.svg
|
||||
== marker-attribute-01.svg pass.svg
|
||||
== marker-viewBox-01.svg marker-viewBox-01-ref.svg
|
||||
== marker-orientation-01.svg marker-orientation-01-ref.svg
|
||||
== mask-basic-01.svg pass.svg
|
||||
== mask-basic-02.svg mask-basic-02-ref.svg
|
||||
== mask-extref-dataURI-01.svg pass.svg
|
||||
|
|
|
@ -1841,6 +1841,8 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
|
|||
NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
|
||||
if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
|
||||
expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
|
||||
} else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
|
||||
expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
|
||||
} else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
|
||||
expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
|
||||
} else {
|
||||
|
|
|
@ -250,17 +250,25 @@ nsMediaExpression::Matches(nsPresContext *aPresContext,
|
|||
case nsMediaFeature::eResolution:
|
||||
{
|
||||
NS_ASSERTION(actual.GetUnit() == eCSSUnit_Inch ||
|
||||
actual.GetUnit() == eCSSUnit_Pixel ||
|
||||
actual.GetUnit() == eCSSUnit_Centimeter,
|
||||
"bad actual value");
|
||||
NS_ASSERTION(required.GetUnit() == eCSSUnit_Inch ||
|
||||
required.GetUnit() == eCSSUnit_Pixel ||
|
||||
required.GetUnit() == eCSSUnit_Centimeter,
|
||||
"bad required value");
|
||||
float actualDPI = actual.GetFloatValue();
|
||||
if (actual.GetUnit() == eCSSUnit_Centimeter)
|
||||
if (actual.GetUnit() == eCSSUnit_Centimeter) {
|
||||
actualDPI = actualDPI * 2.54f;
|
||||
} else if (actual.GetUnit() == eCSSUnit_Pixel) {
|
||||
actualDPI = actualDPI * 96.0f;
|
||||
}
|
||||
float requiredDPI = required.GetFloatValue();
|
||||
if (required.GetUnit() == eCSSUnit_Centimeter)
|
||||
if (required.GetUnit() == eCSSUnit_Centimeter) {
|
||||
requiredDPI = requiredDPI * 2.54f;
|
||||
} else if (required.GetUnit() == eCSSUnit_Pixel) {
|
||||
requiredDPI = requiredDPI * 96.0f;
|
||||
}
|
||||
cmp = DoCompare(actualDPI, requiredDPI);
|
||||
}
|
||||
break;
|
||||
|
@ -432,6 +440,8 @@ nsMediaQuery::AppendToString(nsAString& aString) const
|
|||
aString.AppendFloat(expr.mValue.GetFloatValue());
|
||||
if (expr.mValue.GetUnit() == eCSSUnit_Inch) {
|
||||
aString.AppendLiteral("dpi");
|
||||
} else if (expr.mValue.GetUnit() == eCSSUnit_Pixel) {
|
||||
aString.AppendLiteral("dppx");
|
||||
} else {
|
||||
NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Centimeter,
|
||||
"bad unit");
|
||||
|
|
|
@ -248,9 +248,10 @@ static nsresult
|
|||
GetResolution(nsPresContext* aPresContext, const nsMediaFeature*,
|
||||
nsCSSValue& aResult)
|
||||
{
|
||||
// Resolution values are in device pixels, not CSS pixels.
|
||||
nsDeviceContext *dx = GetDeviceContextFor(aPresContext);
|
||||
float dpi = float(dx->AppUnitsPerPhysicalInch()) / float(dx->AppUnitsPerDevPixel());
|
||||
// Resolution measures device pixels per CSS (inch/cm/pixel). We
|
||||
// return it in device pixels per CSS inches.
|
||||
float dpi = float(nsPresContext::AppUnitsPerCSSInch()) /
|
||||
float(aPresContext->AppUnitsPerDevPixel());
|
||||
aResult.SetFloatValue(dpi, eCSSUnit_Inch);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ struct nsMediaFeature {
|
|||
eFloat, // values are eCSSUnit_Number
|
||||
eBoolInteger,// values are eCSSUnit_Integer (0, -0, or 1 only)
|
||||
eIntRatio, // values are eCSSUnit_Array of two eCSSUnit_Integer
|
||||
eResolution, // values are in eCSSUnit_Inch (for dpi) or
|
||||
eResolution, // values are in eCSSUnit_Inch (for dpi),
|
||||
// eCSSUnit_Pixel (for dppx), or
|
||||
// eCSSUnit_Centimeter (for dpcm)
|
||||
eEnumerated, // values are eCSSUnit_Enumerated (uses keyword table)
|
||||
eIdent // values are eCSSUnit_Ident
|
||||
|
|
|
@ -464,8 +464,12 @@ function run() {
|
|||
expression_should_be_parseable(feature + ": 3.0dpi");
|
||||
expression_should_be_parseable(feature + ": 3.4dpi");
|
||||
expression_should_be_parseable(feature + "\t: 120dpcm");
|
||||
expression_should_be_parseable(feature + ": 1dppx");
|
||||
expression_should_be_parseable(feature + ": 1.5dppx");
|
||||
expression_should_be_parseable(feature + ": 2.0dppx");
|
||||
expression_should_not_be_parseable(feature + ": 0dpi");
|
||||
expression_should_not_be_parseable(feature + ": -3dpi");
|
||||
expression_should_not_be_parseable(feature + ": 0dppx");
|
||||
}
|
||||
|
||||
// Find the resolution using max-resolution
|
||||
|
@ -483,13 +487,16 @@ function run() {
|
|||
var dpi_low = resolution - 1;
|
||||
if (query_applies("(min-resolution: " + resolution + "dpi)")) {
|
||||
// It's exact!
|
||||
is(resolution % 96, 0, "resolution should be a multiple of 96dpi");
|
||||
should_apply("(resolution: " + resolution + "dpi)");
|
||||
should_apply("(resolution: " + Math.floor(resolution/96) + "dppx)");
|
||||
should_not_apply("(resolution: " + (resolution + 1) + "dpi)");
|
||||
should_not_apply("(resolution: " + (resolution - 1) + "dpi)");
|
||||
dpi_high = resolution + 1;
|
||||
} else {
|
||||
// We have no way to test resolution applying since it need not be
|
||||
// an integer.
|
||||
ok(false, "resolution should be a multiple of 96dpi");
|
||||
should_not_apply("(resolution: " + resolution + "dpi)");
|
||||
should_not_apply("(resolution: " + (resolution - 1) + "dpi)");
|
||||
dpi_high = resolution;
|
||||
|
|
|
@ -280,6 +280,19 @@ function run() {
|
|||
mql.removeListener(null);
|
||||
})();
|
||||
|
||||
/* Bug 753777: test that things work in a freshly-created iframe */
|
||||
(function() {
|
||||
var iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
is(iframe.contentWindow.matchMedia("(min-width: 1px)").matches, true,
|
||||
"(min-width: 1px) should match in newly-created iframe");
|
||||
is(iframe.contentWindow.matchMedia("(max-width: 1px)").matches, false,
|
||||
"(max-width: 1px) should not match in newly-created iframe");
|
||||
|
||||
document.body.removeChild(iframe);
|
||||
})();
|
||||
|
||||
/* Bug 716751: listeners lost due to GC */
|
||||
var gc_received = [];
|
||||
(function() {
|
||||
|
|
|
@ -271,6 +271,15 @@ nsSVGDisplayContainerFrame::UpdateBounds()
|
|||
nsSVGEffects::UpdateEffects(this);
|
||||
}
|
||||
|
||||
// We only invalidate if we are dirty, if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be invalidated
|
||||
// when it gets that initial reflow), and if our parent is not dirty (since
|
||||
// if it is, then it will invalidate its entire new area, which will include
|
||||
// our new area).
|
||||
bool invalidate = (mState & NS_FRAME_IS_DIRTY) &&
|
||||
!(GetParent()->GetStateBits() &
|
||||
(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY));
|
||||
|
||||
FinishAndStoreOverflow(overflowRects, mRect.Size());
|
||||
|
||||
// Remove state bits after FinishAndStoreOverflow so that it doesn't
|
||||
|
@ -278,10 +287,7 @@ nsSVGDisplayContainerFrame::UpdateBounds()
|
|||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// We only invalidate if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be
|
||||
// invalidated when it gets that initial reflow):
|
||||
if (invalidate) {
|
||||
// XXXSDL Let FinishAndStoreOverflow do this.
|
||||
nsSVGUtils::InvalidateBounds(this, true);
|
||||
}
|
||||
|
|
|
@ -381,6 +381,15 @@ nsSVGForeignObjectFrame::UpdateBounds()
|
|||
nsSVGEffects::UpdateEffects(this);
|
||||
}
|
||||
|
||||
// We only invalidate if we are dirty, if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be invalidated
|
||||
// when it gets that initial reflow), and if our parent is not dirty (since
|
||||
// if it is, then it will invalidate its entire new area, which will include
|
||||
// our new area).
|
||||
bool invalidate = (mState & NS_FRAME_IS_DIRTY) &&
|
||||
!(GetParent()->GetStateBits() &
|
||||
(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY));
|
||||
|
||||
// TODO: once we support |overflow:visible| on foreignObject, then we will
|
||||
// need to take account of our descendants here.
|
||||
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
|
||||
|
@ -391,10 +400,7 @@ nsSVGForeignObjectFrame::UpdateBounds()
|
|||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// We only invalidate if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be
|
||||
// invalidated when it gets that initial reflow):
|
||||
if (invalidate) {
|
||||
// XXXSDL Let FinishAndStoreOverflow do this.
|
||||
nsSVGUtils::InvalidateBounds(this, true);
|
||||
}
|
||||
|
|
|
@ -490,6 +490,15 @@ nsSVGGlyphFrame::UpdateBounds()
|
|||
mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
|
||||
mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
|
||||
|
||||
// We only invalidate if we are dirty, if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be invalidated
|
||||
// when it gets that initial reflow), and if our parent is not dirty (since
|
||||
// if it is, then it will invalidate its entire new area, which will include
|
||||
// our new area).
|
||||
bool invalidate = (mState & NS_FRAME_IS_DIRTY) &&
|
||||
!(GetParent()->GetStateBits() &
|
||||
(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY));
|
||||
|
||||
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
|
||||
nsOverflowAreas overflowAreas(overflow, overflow);
|
||||
FinishAndStoreOverflow(overflowAreas, mRect.Size());
|
||||
|
@ -497,10 +506,7 @@ nsSVGGlyphFrame::UpdateBounds()
|
|||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// We only invalidate if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be
|
||||
// invalidated when it gets that initial reflow):
|
||||
if (invalidate) {
|
||||
// XXXSDL Let FinishAndStoreOverflow do this.
|
||||
nsSVGUtils::InvalidateBounds(this, true);
|
||||
}
|
||||
|
|
|
@ -496,6 +496,15 @@ nsSVGImageFrame::UpdateBounds()
|
|||
nsSVGEffects::UpdateEffects(this);
|
||||
}
|
||||
|
||||
// We only invalidate if we are dirty, if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be invalidated
|
||||
// when it gets that initial reflow), and if our parent is not dirty (since
|
||||
// if it is, then it will invalidate its entire new area, which will include
|
||||
// our new area).
|
||||
bool invalidate = (mState & NS_FRAME_IS_DIRTY) &&
|
||||
!(GetParent()->GetStateBits() &
|
||||
(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY));
|
||||
|
||||
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
|
||||
nsOverflowAreas overflowAreas(overflow, overflow);
|
||||
FinishAndStoreOverflow(overflowAreas, mRect.Size());
|
||||
|
@ -503,10 +512,7 @@ nsSVGImageFrame::UpdateBounds()
|
|||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// We only invalidate if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be
|
||||
// invalidated when it gets that initial reflow):
|
||||
if (invalidate) {
|
||||
// XXXSDL Let FinishAndStoreOverflow do this.
|
||||
nsSVGUtils::InvalidateBounds(this, true);
|
||||
}
|
||||
|
|
|
@ -239,6 +239,15 @@ nsSVGPathGeometryFrame::UpdateBounds()
|
|||
nsSVGEffects::UpdateEffects(this);
|
||||
}
|
||||
|
||||
// We only invalidate if we are dirty, if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be invalidated
|
||||
// when it gets that initial reflow), and if our parent is not dirty (since
|
||||
// if it is, then it will invalidate its entire new area, which will include
|
||||
// our new area).
|
||||
bool invalidate = (mState & NS_FRAME_IS_DIRTY) &&
|
||||
!(GetParent()->GetStateBits() &
|
||||
(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY));
|
||||
|
||||
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
|
||||
nsOverflowAreas overflowAreas(overflow, overflow);
|
||||
FinishAndStoreOverflow(overflowAreas, mRect.Size());
|
||||
|
@ -246,12 +255,8 @@ nsSVGPathGeometryFrame::UpdateBounds()
|
|||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
// XXXSDL get rid of this in favor of the invalidate call in
|
||||
// FinishAndStoreOverflow?
|
||||
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// We only invalidate if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be
|
||||
// invalidated when it gets that initial reflow):
|
||||
if (invalidate) {
|
||||
// XXXSDL Let FinishAndStoreOverflow do this.
|
||||
nsSVGUtils::InvalidateBounds(this, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,6 +184,15 @@ nsSVGSwitchFrame::UpdateBounds()
|
|||
nsSVGEffects::UpdateEffects(this);
|
||||
}
|
||||
|
||||
// We only invalidate if we are dirty, if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be invalidated
|
||||
// when it gets that initial reflow), and if our parent is not dirty (since
|
||||
// if it is, then it will invalidate its entire new area, which will include
|
||||
// our new area).
|
||||
bool invalidate = (mState & NS_FRAME_IS_DIRTY) &&
|
||||
!(GetParent()->GetStateBits() &
|
||||
(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY));
|
||||
|
||||
FinishAndStoreOverflow(overflowRects, mRect.Size());
|
||||
|
||||
// Remove state bits after FinishAndStoreOverflow so that it doesn't
|
||||
|
@ -191,10 +200,7 @@ nsSVGSwitchFrame::UpdateBounds()
|
|||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// We only invalidate if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be
|
||||
// invalidated when it gets that initial reflow):
|
||||
if (invalidate) {
|
||||
// XXXSDL Let FinishAndStoreOverflow do this.
|
||||
nsSVGUtils::InvalidateBounds(this, true);
|
||||
}
|
||||
|
|
|
@ -231,14 +231,20 @@ nsSVGTextFrame::UpdateBounds()
|
|||
|
||||
UpdateGlyphPositioning(false);
|
||||
|
||||
// We only invalidate if we are dirty, if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be invalidated
|
||||
// when it gets that initial reflow), and if our parent is not dirty (since
|
||||
// if it is, then it will invalidate its entire new area, which will include
|
||||
// our new area).
|
||||
bool invalidate = (mState & NS_FRAME_IS_DIRTY) &&
|
||||
!(GetParent()->GetStateBits() &
|
||||
(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY));
|
||||
|
||||
// With glyph positions updated, our descendants can invalidate their new
|
||||
// areas correctly:
|
||||
nsSVGTextFrameBase::UpdateBounds();
|
||||
|
||||
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// We only invalidate if our outer-<svg> has already had its
|
||||
// initial reflow (since if it hasn't, its entire area will be
|
||||
// invalidated when it gets that initial reflow):
|
||||
if (invalidate) {
|
||||
// XXXSDL Let FinishAndStoreOverflow do this.
|
||||
nsSVGUtils::InvalidateBounds(this, true);
|
||||
}
|
||||
|
|
|
@ -485,6 +485,11 @@ include $(topsrcdir)/config/rules.mk
|
|||
# recursively-expanded variable.
|
||||
ifdef VPX_NEED_OBJ_INT_EXTRACT
|
||||
|
||||
# only for MSVC
|
||||
ifdef _MSC_VER
|
||||
asm_com_offsets.$(OBJ_SUFFIX): CFLAGS += -GL-
|
||||
endif
|
||||
|
||||
asm_com_offsets.asm: asm_com_offsets.$(OBJ_SUFFIX) $(HOST_PROGRAM)
|
||||
./$(HOST_PROGRAM) $(VPX_OIE_FORMAT) $< \
|
||||
$(if $(VPX_AS_CONVERSION),| $(VPX_AS_CONVERSION)) > $@
|
||||
|
@ -495,6 +500,10 @@ OBJS := $(filter-out asm_com_offsets.$(OBJ_SUFFIX),$(OBJS))
|
|||
|
||||
ifdef MOZ_VP8_ENCODER
|
||||
|
||||
ifdef _MSC_VER
|
||||
asm_enc_offsets.$(OBJ_SUFFIX): CFLAGS += -GL-
|
||||
endif
|
||||
|
||||
asm_enc_offsets.asm: asm_enc_offsets.$(OBJ_SUFFIX) $(HOST_PROGRAM)
|
||||
./$(HOST_PROGRAM) $(VPX_OIE_FORMAT) $< \
|
||||
$(if $(VPX_AS_CONVERSION),| $(VPX_AS_CONVERSION)) > $@
|
||||
|
|
|
@ -376,6 +376,7 @@ class TableTicker: public Sampler {
|
|||
|
||||
//XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
|
||||
mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
|
||||
mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
|
||||
mPrimaryThreadProfile.addTag(ProfileEntry('m', "Start"));
|
||||
}
|
||||
|
||||
|
@ -402,6 +403,8 @@ class TableTicker: public Sampler {
|
|||
JSObject *ToJSObject(JSContext *aCx);
|
||||
JSObject *GetMetaJSObject(JSObjectBuilder& b);
|
||||
|
||||
const bool ProfileJS() { return mProfileJS; }
|
||||
|
||||
private:
|
||||
// Not implemented on platforms which do not support backtracing
|
||||
void doBacktrace(ThreadProfile &aProfile, TickSample* aSample);
|
||||
|
@ -413,6 +416,7 @@ private:
|
|||
bool mSaveRequested;
|
||||
bool mUseStackWalk;
|
||||
bool mJankOnly;
|
||||
bool mProfileJS;
|
||||
};
|
||||
|
||||
std::string GetSharedLibraryInfoString();
|
||||
|
@ -683,7 +687,9 @@ void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSampl
|
|||
// 's' tag denotes the start of a sample block
|
||||
// followed by 0 or more 'c' tags.
|
||||
aProfile.addTag(ProfileEntry('s', "(root)"));
|
||||
for (mozilla::sig_safe_t i = 0; i < aStack->mStackPointer; i++) {
|
||||
for (mozilla::sig_safe_t i = 0;
|
||||
i < aStack->mStackPointer && i < mozilla::ArrayLength(aStack->mStack);
|
||||
i++) {
|
||||
// First entry has tagName 's' (start)
|
||||
// Check for magic pointer bit 1 to indicate copy
|
||||
const char* sampleLabel = aStack->mStack[i].mLabel;
|
||||
|
@ -906,6 +912,7 @@ const char** mozilla_sampler_get_features()
|
|||
"stackwalk",
|
||||
#endif
|
||||
"jank",
|
||||
"js",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -931,6 +938,8 @@ void mozilla_sampler_start(int aProfileEntries, int aInterval,
|
|||
aFeatures, aFeatureCount);
|
||||
tlsTicker.set(t);
|
||||
t->Start();
|
||||
if (t->ProfileJS())
|
||||
stack->installJSSampling();
|
||||
}
|
||||
|
||||
void mozilla_sampler_stop()
|
||||
|
@ -943,9 +952,16 @@ void mozilla_sampler_stop()
|
|||
return;
|
||||
}
|
||||
|
||||
bool uninstallJS = t->ProfileJS();
|
||||
|
||||
t->Stop();
|
||||
delete t;
|
||||
tlsTicker.set(NULL);
|
||||
ProfileStack *stack = tlsStack.get();
|
||||
ASSERT(stack != NULL);
|
||||
|
||||
if (uninstallJS)
|
||||
stack->uninstallJSSampling();
|
||||
}
|
||||
|
||||
bool mozilla_sampler_is_active()
|
||||
|
|
|
@ -12,6 +12,14 @@
|
|||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Util.h"
|
||||
|
||||
/* QT has a #define for the word "slots" and jsfriendapi.h has a struct with
|
||||
* this variable name, causing compilation problems. Alleviate this for now by
|
||||
* removing this #define */
|
||||
#ifdef MOZ_WIDGET_QT
|
||||
#undef slots
|
||||
#endif
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
using mozilla::TimeStamp;
|
||||
using mozilla::TimeDuration;
|
||||
|
||||
|
@ -225,7 +233,6 @@ public:
|
|||
ProfileStack()
|
||||
: mStackPointer(0)
|
||||
, mMarkerPointer(0)
|
||||
, mDroppedStackEntries(0)
|
||||
, mQueueClearMarker(false)
|
||||
{ }
|
||||
|
||||
|
@ -273,7 +280,7 @@ public:
|
|||
void push(const char *aName, void *aStackAddress, bool aCopy)
|
||||
{
|
||||
if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
|
||||
mDroppedStackEntries++;
|
||||
mStackPointer++;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -288,29 +295,47 @@ public:
|
|||
}
|
||||
void pop()
|
||||
{
|
||||
if (mDroppedStackEntries > 0) {
|
||||
mDroppedStackEntries--;
|
||||
} else {
|
||||
mStackPointer--;
|
||||
}
|
||||
mStackPointer--;
|
||||
}
|
||||
bool isEmpty()
|
||||
{
|
||||
return mStackPointer == 0;
|
||||
}
|
||||
|
||||
void sampleRuntime(JSRuntime *runtime) {
|
||||
mRuntime = runtime;
|
||||
}
|
||||
void installJSSampling() {
|
||||
JS_STATIC_ASSERT(sizeof(mStack[0]) == sizeof(js::ProfileEntry));
|
||||
js::SetRuntimeProfilingStack(mRuntime,
|
||||
(js::ProfileEntry*) mStack,
|
||||
(uint32_t*) &mStackPointer,
|
||||
mozilla::ArrayLength(mStack));
|
||||
}
|
||||
void uninstallJSSampling() {
|
||||
js::SetRuntimeProfilingStack(mRuntime, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
// Keep a list of active checkpoints
|
||||
StackEntry volatile mStack[1024];
|
||||
// Keep a list of active markers to be applied to the next sample taken
|
||||
char const * volatile mMarkers[1024];
|
||||
volatile mozilla::sig_safe_t mStackPointer;
|
||||
volatile mozilla::sig_safe_t mMarkerPointer;
|
||||
volatile mozilla::sig_safe_t mDroppedStackEntries;
|
||||
// We don't want to modify _markers from within the signal so we allow
|
||||
// it to queue a clear operation.
|
||||
volatile mozilla::sig_safe_t mQueueClearMarker;
|
||||
// The runtime which is being sampled
|
||||
JSRuntime *mRuntime;
|
||||
};
|
||||
|
||||
inline ProfileStack* mozilla_profile_stack(void)
|
||||
{
|
||||
if (!stack_key_initialized)
|
||||
return NULL;
|
||||
return tlsStack.get();
|
||||
}
|
||||
|
||||
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress, bool aCopy)
|
||||
{
|
||||
// check if we've been initialized to avoid calling pthread_getspecific
|
||||
|
|
Загрузка…
Ссылка в новой задаче