Merge last green PGO from inbound to central

This commit is contained in:
Marco Bonardo 2012-02-13 17:17:45 +01:00
commit b2bb1f78ee
52 changed files with 861 additions and 328 deletions

View File

@ -287,11 +287,12 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
// if it's still focused and then update active item and emit focus event.
nsAccessible* target = aEvent->GetAccessible();
if (target != mActiveItem) {
// Check if still focused. Otherwise we can end up with storing the active
// item for control that isn't focused anymore.
nsAccessible* DOMFocus =
GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(),
aEvent->GetDocAccessible());
nsDocAccessible* document = aEvent->GetDocAccessible();
nsAccessible* DOMFocus = document->GetAccessibleOrContainer(FocusedDOMNode());
if (target != DOMFocus)
return;

View File

@ -669,9 +669,8 @@ NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
void
NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
{
nsAccessible* container =
GetAccService()->GetContainerAccessible(aEvent->mNode,
aEvent->GetDocAccessible());
nsDocAccessible* document = aEvent->GetDocAccessible();
nsAccessible* container = document->GetContainerAccessible(aEvent->mNode);
if (!container)
return;

View File

@ -283,38 +283,6 @@ nsAccessNode::ScrollTo(PRUint32 aScrollType)
nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
}
// nsAccessNode public
already_AddRefed<nsINode>
nsAccessNode::GetCurrentFocus()
{
// XXX: consider to use nsFocusManager directly, it allows us to avoid
// unnecessary query interface calls.
nsIDocument* doc = GetDocumentNode();
NS_ENSURE_TRUE(doc, nsnull);
nsIDOMWindow* win = doc->GetWindow();
nsCOMPtr<nsIDOMWindow> focusedWindow;
nsCOMPtr<nsIDOMElement> focusedElement;
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
if (fm)
fm->GetFocusedElementForWindow(win, true, getter_AddRefs(focusedWindow),
getter_AddRefs(focusedElement));
nsINode *focusedNode = nsnull;
if (focusedElement) {
CallQueryInterface(focusedElement, &focusedNode);
}
else if (focusedWindow) {
nsCOMPtr<nsIDOMDocument> doc;
focusedWindow->GetDocument(getter_AddRefs(doc));
if (doc)
CallQueryInterface(doc, &focusedNode);
}
return focusedNode;
}
void
nsAccessNode::Language(nsAString& aLanguage)
{

View File

@ -95,14 +95,6 @@ public:
*/
nsRootAccessible* RootAccessible() const;
/**
* Return focused node within accessible window.
*
* XXX: it shouldn't break us if we return focused node not depending on
* window so that we can turn this method into util method.
*/
already_AddRefed<nsINode> GetCurrentFocus();
/**
* Initialize the access node object, add it to the cache.
*/

View File

@ -919,17 +919,6 @@ nsAccessibilityService::GetAccessible(nsINode* aNode, nsIPresShell* aPresShell)
return document ? document->GetAccessible(aNode) : nsnull;
}
nsAccessible*
nsAccessibilityService::GetAccessibleOrContainer(nsINode* aNode, nsDocAccessible* aDoc)
{
if (!aNode)
return nsnull;
NS_PRECONDITION(aDoc, "Must pass a document accessible.");
return aDoc ? aDoc->GetAccessibleOrContainer(aNode) : nsnull;
}
static bool HasRelatedContent(nsIContent *aContent)
{
nsAutoString id;

View File

@ -197,24 +197,6 @@ public:
*/
nsAccessible* GetAccessible(nsINode* aNode, nsIPresShell* aPresShell);
/**
* Return an accessible for the given DOM node or container accessible if
* the node is not accessible.
*
* @param aNode [in] the given node
* @param aDoc [in] the document accessible. Can't be null.
*/
nsAccessible* GetAccessibleOrContainer(nsINode* aNode, nsDocAccessible* aDoc);
/**
* Return a container accessible for the given DOM node.
*/
nsAccessible* GetContainerAccessible(nsINode* aNode, nsDocAccessible* aDoc)
{
return aNode ?
GetAccessibleOrContainer(aNode->GetNodeParent(), aDoc) : nsnull;
}
private:
// nsAccessibilityService creation is controlled by friend
// NS_GetAccessibilityService, keep constructors private.

View File

@ -852,8 +852,7 @@ nsAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
// Get accessible for the node with the point or the first accessible in
// the DOM parent chain.
nsAccessible* accessible =
GetAccService()->GetAccessibleOrContainer(content, accDocument);
nsAccessible* accessible = accDocument->GetAccessibleOrContainer(content);
if (!accessible)
return fallbackAnswer;
@ -1147,7 +1146,7 @@ nsAccessible::TakeFocus()
}
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm)
fm->SetFocus(element, 0);

View File

@ -378,7 +378,7 @@ NS_IMETHODIMP nsDocAccessible::TakeFocus()
return NS_ERROR_FAILURE;
// Focus the document.
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_STATE(fm);
nsCOMPtr<nsIDOMElement> newFocus;

View File

@ -203,7 +203,7 @@ nsRootAccessible::NativeState()
states |= states::MODAL;
#endif
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
nsCOMPtr<nsIDOMWindow> rootWindow;
GetWindow(getter_AddRefs(rootWindow));

View File

@ -1628,8 +1628,16 @@ nsHyperTextAccessible::SetCaretOffset(PRInt32 aCaretOffset)
NS_IMETHODIMP
nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
{
NS_ENSURE_ARG_POINTER(aCaretOffset);
*aCaretOffset = -1;
// Not focused focusable accessible except document accessible doesn't have
// a caret.
if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
(State() & states::FOCUSABLE)) {
return NS_OK;
}
// No caret if the focused node is not inside this DOM node and this DOM node
// is not inside of focused node.
FocusManager::FocusDisposition focusDisp =

View File

@ -46,21 +46,8 @@
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
function testCaretOffset(aAccOrElmOrID, aCaretOffset)
{
var acc = getAccessible(aAccOrElmOrID, [nsIAccessibleText]);
is(acc.caretOffset, aCaretOffset,
"Wrong caret offset for " + aAccOrElmOrID);
}
function doTests()
{
todo(false, "enable commented tests Bug 510128 is fixed");
// test no focused accessibles
//testCaretOffset("textbox", -1);
//testCaretOffset("textarea", -1);
testCaretOffset("p", -1);
// test caret move events and caret offsets
gQueue = new eventQueue();

View File

@ -25,6 +25,16 @@
prefs.setBoolPref("accessibility.browsewithcaret", aIsOn);
}
/**
* Test caret offset for the given accessible.
*/
function testCaretOffset(aID, aCaretOffset)
{
var acc = getAccessible(aID, [nsIAccessibleText]);
is(acc.caretOffset, aCaretOffset,
"Wrong caret offset for " + aID);
}
/**
* Do tests.
*/
@ -37,6 +47,12 @@
{
turnCaretBrowsing(true);
// test caret offsets
testCaretOffset(document, 14);
testCaretOffset("textbox", -1);
testCaretOffset("textarea", -1);
testCaretOffset("p", -1);
// test caret move events and caret offsets
gQueue = new eventQueue();
@ -58,6 +74,11 @@
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=448744"
title="caretOffset should return -1 if the system caret is not currently with in that particular object">
Mozilla Bug 448744
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=524115"
title="HyperText accessible should get focus when the caret is positioned inside of it, text is changed or copied into clipboard by ATs">
@ -68,12 +89,19 @@
title="Position is not being updated when atk_text_set_caret_offset is used">
Mozilla Bug 546068
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=725581"
title="caretOffset for textarea should be -1 when textarea doesn't have a focus">
Mozilla Bug 725581
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<input id="textbox" value="hello"/>
<textarea id="textarea">text<br>text</textarea>
<p id="p" contentEditable="true"><span>text</span><br/>text</p>
<a id="link" href="about:">about mozilla</a>
<h5 id="heading">heading</h5>

View File

@ -8211,7 +8211,7 @@ dnl ========================================================
dnl qcms
dnl ========================================================
QCMS_LIBS='$(call EXPAND_LIBNAME_PATH,mozqcms,$(DEPTH)/gfx/qcms)'
QCMS_LIBS='$(DEPTH)/gfx/qcms/$(LIB_PREFIX)mozqcms.$(LIB_SUFFIX)'
AC_SUBST(QCMS_LIBS)
dnl ========================================================

View File

@ -266,7 +266,7 @@ AsyncClickHandler::Run()
}
// Check if page is allowed to open the popup
if (mPopupControlState != openAllowed) {
if (mPopupControlState > openControlled) {
nsCOMPtr<nsIPopupWindowManager> pm =
do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);

View File

@ -763,7 +763,9 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
if (BasicManager()->IsTransactionIncomplete())
return;
if (!IsHidden()) {
gfxRect clipExtents;
clipExtents = aContext->GetClipExtents();
if (!IsHidden() && !clipExtents.IsEmpty()) {
AutoSetOperator setOperator(aContext, GetOperator());
mBuffer.DrawTo(this, aContext, opacity);
}
@ -1946,6 +1948,11 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
NS_ABORT_IF_FALSE(untransformedSurface,
"We should always allocate an untransformed surface with 3d transforms!");
// Temporary fast fix for bug 725886
// Revert these changes when 725886 is ready
gfxRect clipExtents;
clipExtents = aTarget->GetClipExtents();
if (!clipExtents.IsEmpty()) {
gfxPoint offset;
bool dontBlit = needsClipToVisibleRegion || mTransactionIncomplete ||
aLayer->GetEffectiveOpacity() != 1.0f;
@ -1958,6 +1965,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
aTarget->SetSource(result, offset);
}
}
}
// If we're doing our own double-buffering, we need to avoid drawing
// the results of an incomplete transaction to the destination surface ---
// that could cause flicker. Double-buffering is implemented using a

View File

@ -44,7 +44,6 @@ MODULE = qcms
LIBRARY_NAME = mozqcms
LIBXUL_LIBRARY = 1
GRE_MODULE = 1
DIST_INSTALL = 1
EXPORTS = qcms.h qcmstypes.h

View File

@ -604,7 +604,7 @@ check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL
# We desire these numbers to go down, not up. See "User guide to memory
# management within SpiderMonkey" in jsutil.h.
$(srcdir)/config/check_source_count.py OffTheBooks:: 58 \
$(srcdir)/config/check_source_count.py OffTheBooks:: 71 \
"in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^
# This should go to zero, if possible.
$(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \

View File

@ -0,0 +1,21 @@
// |jit-test| mjitalways;
gczeal(4);
var optionNames = options().split(',');
for (var i = 0; i < optionNames.length; i++)
var optionName = optionNames[i];
options(optionName);
evaluate("\
function addDebug(g, id) {\
var debuggerGlobal = newGlobal('new-compartment');\
debuggerGlobal.debuggee = g;\
debuggerGlobal.id = id;\
debuggerGlobal.print = function (s) { (g) += s; };\
debuggerGlobal.eval('var dbg = new Debugger(debuggee);dbg.onDebuggerStatement = function () { print(id); debugger; };');\
return debuggerGlobal;\
}\
var base = newGlobal('new-compartment');\
var top = base;\
for (var i = 0; i < 8; i++ )\
top = addDebug(top, i);\
base.eval('debugger;');\
");

View File

@ -0,0 +1,13 @@
gczeal(4);
var BUGNUMBER = 668024;
var summary =
print(BUGNUMBER + ": " + summary);
var arr = [0, 1, 2, 3, 4, 5, , 7];
var seen = [];
for (var p in arr) {
if (seen.indexOf(unescape) >= 0) {}
arr.splice(2, 3);
seen.push(p);
}

View File

@ -940,12 +940,15 @@ ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offs
/*
* The variable is live even before the write, due to an enclosing try
* block. We need to split the lifetime to indicate there was a write.
* We set the new interval's savedEnd to 0, since it will always be
* adjacent to the old interval, so it never needs to be extended.
*/
var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(start, offset, var.lifetime);
var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(start, 0, var.lifetime);
if (!var.lifetime) {
setOOM(cx);
return;
}
var.lifetime->end = offset;
} else {
var.saved = var.lifetime;
var.savedEnd = 0;
@ -973,25 +976,43 @@ ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var,
var.lifetime->start = start;
/*
* When walking backwards through loop bodies, we don't know which vars
* are live at the loop's backedge. We save the endpoints for lifetime
* segments which we *would* use if the variables were live at the backedge
* and extend the variable with new lifetimes if we find the variable is
* indeed live at the head of the loop.
* Consider this code:
*
* while (...) {
* if (x #1) { ... }
* while (...) { (#1)
* use x; (#2)
* ...
* if (... #2) { x = 0; #3}
* }
* x = ...; (#3)
* ...
* } (#4)
*
* If x is not live after the loop, we treat it as dead in the walk and
* make a point lifetime for the write at #3. At the beginning of that
* basic block (#2), we save the loop endpoint; if we knew x was live in
* the next iteration then a new lifetime would be made here. At #1 we
* mark x live again, make a segment between the head of the loop and #1,
* and then extend x with loop tail lifetimes from #1 to #2, and from #3
* to the back edge.
* Just before analyzing the while statement, there would be a live range
* from #1..#2 and a "point range" at #3. The job of extendVariable is to
* create a new live range from #3..#4.
*
* However, more extensions may be required if the definition of x is
* conditional. Consider the following.
*
* while (...) { (#1)
* use x; (#2)
* ...
* if (...) (#5)
* x = ...; (#3)
* ...
* } (#4)
*
* Assume that x is not used after the loop. Then, before extendVariable is
* run, the live ranges would be the same as before (#1..#2 and #3..#3). We
* still need to create a range from #3..#4. But, since the assignment at #3
* may never run, we also need to create a range from #2..#3. This is done
* as follows.
*
* Each time we create a Lifetime, we store the start of the most recently
* seen sequence of conditional code in the Lifetime's savedEnd field. So,
* when creating the Lifetime at #2, we set the Lifetime's savedEnd to
* #5. (The start of the most recent conditional is cached in each
* variable's savedEnd field.) Consequently, extendVariable is able to
* create a new interval from #2..#5 using the savedEnd field of the
* existing #1..#2 interval.
*/
Lifetime *segment = var.lifetime;

View File

@ -720,8 +720,6 @@ JSRuntime::JSRuntime()
gcUserAvailableChunkListHead(NULL),
gcKeepAtoms(0),
gcBytes(0),
gcTriggerBytes(0),
gcLastBytes(0),
gcMaxBytes(0),
gcMaxMallocBytes(0),
gcNumArenasFreeCommitted(0),

View File

@ -284,8 +284,6 @@ struct JSRuntime : js::RuntimeFriendFields
js::GCLocks gcLocksHash;
jsrefcount gcKeepAtoms;
size_t gcBytes;
size_t gcTriggerBytes;
size_t gcLastBytes;
size_t gcMaxBytes;
size_t gcMaxMallocBytes;
@ -540,9 +538,6 @@ struct JSRuntime : js::RuntimeFriendFields
JSRuntime *thisFromCtor() { return this; }
void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);
void reduceGCTriggerBytes(size_t amount);
/*
* Call the system malloc while checking for GC memory pressure and
* reporting OOM error when cx is not null. We will not GC from here.

View File

@ -784,10 +784,8 @@ Chunk::releaseArena(ArenaHeader *aheader)
JS_ASSERT(rt->gcBytes >= ArenaSize);
JS_ASSERT(comp->gcBytes >= ArenaSize);
#ifdef JS_THREADSAFE
if (rt->gcHelperThread.sweeping()) {
rt->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
if (rt->gcHelperThread.sweeping())
comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
}
#endif
rt->gcBytes -= ArenaSize;
comp->gcBytes -= ArenaSize;
@ -893,12 +891,6 @@ js_InitGC(JSRuntime *rt, uint32_t maxbytes)
rt->gcMaxBytes = maxbytes;
rt->setGCMaxMallocBytes(maxbytes);
/*
* The assigned value prevents GC from running when GC memory is too low
* (during JS engine start).
*/
rt->setGCLastBytes(8192, GC_NORMAL);
rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
return true;
}
@ -1355,25 +1347,6 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
return ct;
}
void
JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
{
gcLastBytes = lastBytes;
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger));
}
void
JSRuntime::reduceGCTriggerBytes(size_t amount) {
JS_ASSERT(amount > 0);
JS_ASSERT(gcTriggerBytes - amount >= 0);
if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
return;
gcTriggerBytes -= amount;
}
void
JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
{
@ -1385,7 +1358,8 @@ JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
}
void
JSCompartment::reduceGCTriggerBytes(size_t amount) {
JSCompartment::reduceGCTriggerBytes(size_t amount)
{
JS_ASSERT(amount > 0);
JS_ASSERT(gcTriggerBytes - amount >= 0);
if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
@ -1891,26 +1865,6 @@ gc_lock_traversal(const GCLocks::Entry &entry, JSTracer *trc)
MarkRootGCThing(trc, entry.key, "locked object");
}
void
js_TraceStackFrame(JSTracer *trc, StackFrame *fp)
{
MarkRoot(trc, &fp->scopeChain(), "scope chain");
if (fp->isDummyFrame())
return;
if (fp->hasArgsObj())
MarkRoot(trc, &fp->argsObj(), "arguments");
if (fp->isFunctionFrame()) {
MarkRoot(trc, fp->fun(), "fun");
if (fp->isEvalFrame()) {
MarkRoot(trc, fp->script(), "eval script");
}
} else {
MarkRoot(trc, fp->script(), "script");
}
fp->script()->compartment()->active = true;
MarkRoot(trc, fp->returnValue(), "rval");
}
void
AutoIdArray::trace(JSTracer *trc)
{
@ -2121,6 +2075,12 @@ MarkRuntime(JSTracer *trc)
}
}
#ifdef JS_METHODJIT
/* We need to expand inline frames before stack scanning. */
for (CompartmentsIter c(rt); !c.done(); c.next())
mjit::ExpandInlineFrames(c);
#endif
rt->stackSpace.mark(trc);
/* The embedding can register additional roots here. */
@ -2173,12 +2133,6 @@ TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason)
return;
}
if (rt->gcBytes > 8192 && rt->gcBytes >= 3 * (rt->gcTriggerBytes / 2)) {
/* If we're using significantly more than our quota, do a full GC. */
TriggerGC(rt, reason);
return;
}
/*
* Trigger the GC when it is safe to call an operation callback on any
* thread.
@ -2966,7 +2920,6 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
#endif
rt->gcMarkAndSweep = false;
rt->setGCLastBytes(rt->gcBytes, gckind);
rt->gcCurrentCompartment = NULL;
for (CompartmentsIter c(rt); !c.done(); c.next())

View File

@ -1371,9 +1371,6 @@ IsAboutToBeFinalized(const js::gc::Cell *thing);
extern bool
IsAboutToBeFinalized(const js::Value &value);
extern void
js_TraceStackFrame(JSTracer *trc, js::StackFrame *fp);
extern bool
js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind, void **thing);

View File

@ -1029,6 +1029,12 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, StringPredicate pred
for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
*p = *(p + 1);
ni->props_end = ni->end() - 1;
/*
* Invoke the write barrier on this element, since it's
* no longer going to be marked.
*/
ni->props_end->HeapPtr<JSFlatString>::~HeapPtr<JSFlatString>();
}
/* Don't reuse modified native iterators. */
@ -1379,7 +1385,7 @@ MarkGenerator(JSTracer *trc, JSGenerator *gen)
* this code and save someone an hour later.
*/
MarkStackRangeConservatively(trc, gen->floatingStack, fp->formalArgsEnd());
js_TraceStackFrame(trc, fp);
fp->mark(trc);
MarkStackRangeConservatively(trc, fp->slots(), gen->regs.sp);
}

View File

@ -410,7 +410,7 @@ mjit::Compiler::pushActiveFrame(JSScript *script, uint32_t argc)
if (cx->runtime->profilingScripts && !script->pcCounters)
script->initCounts(cx);
ActiveFrame *newa = cx->new_<ActiveFrame>(cx);
ActiveFrame *newa = OffTheBooks::new_<ActiveFrame>(cx);
if (!newa)
return Compile_Error;
@ -459,7 +459,7 @@ mjit::Compiler::pushActiveFrame(JSScript *script, uint32_t argc)
return Compile_Error;
}
newa->jumpMap = (Label *)cx->malloc_(sizeof(Label) * script->length);
newa->jumpMap = (Label *)OffTheBooks::malloc_(sizeof(Label) * script->length);
if (!newa->jumpMap) {
js_ReportOutOfMemory(cx);
return Compile_Error;
@ -618,7 +618,7 @@ mjit::Compiler::prepareInferenceTypes(JSScript *script, ActiveFrame *a)
*/
a->varTypes = (VarType *)
cx->calloc_(TotalSlots(script) * sizeof(VarType));
OffTheBooks::calloc_(TotalSlots(script) * sizeof(VarType));
if (!a->varTypes)
return Compile_Error;
@ -871,7 +871,7 @@ MakeJITScript(JSContext *cx, JSScript *script, bool construct)
size_t dataSize = sizeof(JITScript)
+ (chunks.length() * sizeof(ChunkDescriptor))
+ (edges.length() * sizeof(CrossChunkEdge));
uint8_t *cursor = (uint8_t *) cx->calloc_(dataSize);
uint8_t *cursor = (uint8_t *) OffTheBooks::calloc_(dataSize);
if (!cursor)
return NULL;
@ -1204,7 +1204,7 @@ mjit::Compiler::generatePrologue()
if (outerScript->pcCounters || Probes::wantNativeAddressInfo(cx)) {
size_t length = ssa.frameLength(ssa.numFrames() - 1);
pcLengths = (PCLengthEntry *) cx->calloc_(sizeof(pcLengths[0]) * length);
pcLengths = (PCLengthEntry *) OffTheBooks::calloc_(sizeof(pcLengths[0]) * length);
if (!pcLengths)
return Compile_Error;
}
@ -1352,7 +1352,7 @@ mjit::Compiler::finishThisUp()
#endif
0;
uint8_t *cursor = (uint8_t *)cx->calloc_(dataSize);
uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(dataSize);
if (!cursor) {
execPool->release();
js_ReportOutOfMemory(cx);
@ -1804,7 +1804,7 @@ mjit::Compiler::finishThisUp()
ChunkJumpTableEdge nedge = chunkJumps[j];
if (nedge.edge.source == edge.source && nedge.edge.target == edge.target) {
if (!jumpTableEntries) {
jumpTableEntries = cx->new_<CrossChunkEdge::JumpTableEntryVector>();
jumpTableEntries = OffTheBooks::new_<CrossChunkEdge::JumpTableEntryVector>();
if (!jumpTableEntries)
failed = true;
}
@ -5627,16 +5627,18 @@ mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed)
if (script->pcCounters)
bumpPropCounter(PC, OpcodeCounts::PROP_OTHER);
JSOp op = JSOp(*PC);
#ifdef JSGC_INCREMENTAL_MJ
/* Write barrier. */
if (cx->compartment->needsBarrier() && (!types || types->propertyNeedsBarrier(cx, id))) {
/* Write barrier. We don't have type information for JSOP_SETNAME. */
if (cx->compartment->needsBarrier() &&
(!types || op == JSOP_SETNAME || types->propertyNeedsBarrier(cx, id)))
{
jsop_setprop_slow(name);
return true;
}
#endif
JSOp op = JSOp(*PC);
ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
? ic::PICInfo::SETMETHOD
: ic::PICInfo::SET;
@ -6978,7 +6980,7 @@ mjit::Compiler::startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
loop->clearLoopRegisters();
}
LoopState *nloop = cx->new_<LoopState>(cx, &ssa, this, &frame);
LoopState *nloop = OffTheBooks::new_<LoopState>(cx, &ssa, this, &frame);
if (!nloop || !nloop->init(head, entry, entryTarget))
return false;

View File

@ -94,7 +94,7 @@ FrameState::pushActiveFrame(JSScript *script, uint32_t argc)
size_t totalBytes = sizeof(FrameEntry) * nentries + // entries[]
sizeof(FrameEntry *) * nentries + // tracker.entries
sizeof(StackEntryExtra) * nentries; // extraArray
uint8_t *cursor = (uint8_t *)cx->calloc_(totalBytes);
uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(totalBytes);
if (!cursor)
return false;
@ -120,7 +120,7 @@ FrameState::pushActiveFrame(JSScript *script, uint32_t argc)
/* We should have already checked that argc == nargs */
JS_ASSERT_IF(a, argc == script->function()->nargs);
ActiveFrame *newa = cx->new_<ActiveFrame>();
ActiveFrame *newa = OffTheBooks::new_<ActiveFrame>();
if (!newa)
return false;
@ -2885,7 +2885,7 @@ FrameState::getTemporaryCopies(Uses uses)
FrameEntry *nfe = tracker[i];
if (!deadEntry(nfe, uses.nuses) && nfe->isCopy() && nfe->copyOf() == fe) {
if (!res)
res = cx->new_< Vector<TemporaryCopy> >(cx);
res = OffTheBooks::new_< Vector<TemporaryCopy> >(cx);
res->append(TemporaryCopy(addressOf(nfe), addressOf(fe)));
}
}

View File

@ -64,7 +64,7 @@ ImmutableSync::init(JSContext *cx, const FrameState &frame, uint32_t nentries)
this->cx = cx;
this->frame = &frame;
entries = (SyncEntry *)cx->calloc_(sizeof(SyncEntry) * nentries);
entries = (SyncEntry *)OffTheBooks::calloc_(sizeof(SyncEntry) * nentries);
return !!entries;
}

View File

@ -197,7 +197,7 @@ class BasePolyIC : public BaseIC {
if (isOnePool()) {
JSC::ExecutablePool *oldPool = u.execPool;
JS_ASSERT(!isTagged(oldPool));
ExecPoolVector *execPools = cx->new_<ExecPoolVector>(SystemAllocPolicy());
ExecPoolVector *execPools = OffTheBooks::new_<ExecPoolVector>(SystemAllocPolicy());
if (!execPools)
return false;
if (!execPools->append(oldPool) || !execPools->append(pool)) {

View File

@ -3885,6 +3885,10 @@ MJitChunkLimit(JSContext *cx, uintN argc, jsval *vp)
mjit::SetChunkLimit((uint32_t) t);
#endif
// Clear out analysis information which might refer to code compiled with
// the previous chunk limit.
JS_GC(cx);
vp->setUndefined();
return true;
}

View File

@ -211,6 +211,31 @@ StackFrame::pcQuadratic(const ContextStack &stack, StackFrame *next, JSInlinedSi
return next->prevpc(pinlined);
}
void
StackFrame::mark(JSTracer *trc)
{
/*
* Normally we would use MarkRoot here, except that generators also take
* this path. However, generators use a special write barrier when the stack
* frame is copied to the floating frame. Therefore, no barrier is needed.
*/
gc::MarkObjectUnbarriered(trc, &scopeChain(), "scope chain");
if (isDummyFrame())
return;
if (hasArgsObj())
gc::MarkObjectUnbarriered(trc, &argsObj(), "arguments");
if (isFunctionFrame()) {
gc::MarkObjectUnbarriered(trc, fun(), "fun");
if (isEvalFrame())
gc::MarkScriptUnbarriered(trc, script(), "eval script");
} else {
gc::MarkScriptUnbarriered(trc, script(), "script");
}
if (IS_GC_MARKING_TRACER(trc))
script()->compartment()->active = true;
gc::MarkValueUnbarriered(trc, returnValue(), "rval");
}
/*****************************************************************************/
bool
@ -381,6 +406,51 @@ StackSpace::containingSegment(const StackFrame *target) const
return *(StackSegment *)NULL;
}
void
StackSpace::markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
{
Value *slotsBegin = fp->slots();
if (!fp->isScriptFrame()) {
JS_ASSERT(fp->isDummyFrame());
gc::MarkRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
return;
}
/* If it's a scripted frame, we should have a pc. */
JS_ASSERT(pc);
JSScript *script = fp->script();
if (!script->hasAnalysis() || !script->analysis()->ranLifetimes()) {
gc::MarkRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
return;
}
/*
* If the JIT ran a lifetime analysis, then it may have left garbage in the
* slots considered not live. We need to avoid marking them. Additionally,
* in case the analysis information is thrown out later, we overwrite these
* dead slots with valid values so that future GCs won't crash. Analysis
* results are thrown away during the sweeping phase, so we always have at
* least one GC to do this.
*/
analyze::AutoEnterAnalysis aea(script->compartment());
analyze::ScriptAnalysis *analysis = script->analysis();
uint32_t offset = pc - script->code;
Value *fixedEnd = slotsBegin + script->nfixed;
for (Value *vp = slotsBegin; vp < fixedEnd; vp++) {
uint32_t slot = analyze::LocalSlot(script, vp - slotsBegin);
/* Will this slot be synced by the JIT? */
if (!analysis->trackSlot(slot) || analysis->liveness(slot).live(offset))
gc::MarkRoot(trc, *vp, "vm_stack");
else
*vp = UndefinedValue();
}
gc::MarkRootRange(trc, fixedEnd, slotsEnd, "vm_stack");
}
void
StackSpace::mark(JSTracer *trc)
{
@ -401,15 +471,21 @@ StackSpace::mark(JSTracer *trc)
* calls. Thus, marking can view the stack as the regex:
* (segment slots (frame slots)*)*
* which gets marked in reverse order.
*
*/
Value *slotsEnd = nextSegEnd;
jsbytecode *pc = seg->maybepc();
for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) {
MarkStackRangeConservatively(trc, fp->slots(), slotsEnd);
js_TraceStackFrame(trc, fp);
/* Mark from fp->slots() to slotsEnd. */
markFrameSlots(trc, fp, slotsEnd, pc);
fp->mark(trc);
slotsEnd = (Value *)fp;
JSInlinedSite *site;
pc = fp->prevpc(&site);
JS_ASSERT_IF(fp->prev(), !site);
}
MarkStackRangeConservatively(trc, seg->slotsBegin(), slotsEnd);
gc::MarkRootRange(trc, seg->slotsBegin(), slotsEnd, "vm_stack");
nextSegEnd = (Value *)seg;
}
}

View File

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=79 ft=cpp:
*
* ***** BEGIN LICENSE BLOCK *****
@ -1193,6 +1193,9 @@ class StackFrame
#endif
void methodjitStaticAsserts();
public:
void mark(JSTracer *trc);
};
static const size_t VALUES_PER_STACK_FRAME = sizeof(StackFrame) / sizeof(Value);
@ -1365,6 +1368,10 @@ class StackSegment
return regs_ ? regs_->fp() : NULL;
}
jsbytecode *maybepc() const {
return regs_ ? regs_->pc : NULL;
}
CallArgsList &calls() const {
JS_ASSERT(calls_);
return *calls_;
@ -1535,6 +1542,7 @@ class StackSpace
/* Called during GC: mark segments, frames, and slots under firstUnused. */
void mark(JSTracer *trc);
void markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc);
/* We only report the committed size; uncommitted size is uninteresting. */
JS_FRIEND_API(size_t) sizeOfCommitted();

View File

@ -582,11 +582,19 @@ inline void XPCNativeSet::ASSERT_NotMarked()
/***************************************************************************/
inline
JSObject* XPCWrappedNativeTearOff::GetJSObject() const
JSObject* XPCWrappedNativeTearOff::GetJSObjectPreserveColor() const
{
return mJSObject;
}
inline
JSObject* XPCWrappedNativeTearOff::GetJSObject()
{
JSObject *obj = GetJSObjectPreserveColor();
xpc_UnmarkGrayObject(obj);
return obj;
}
inline
void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj)
{
@ -596,7 +604,7 @@ void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj)
inline
XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff()
{
NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObject()), "tearoff not empty in dtor");
NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObjectPreserveColor()), "tearoff not empty in dtor");
}
/***************************************************************************/
@ -621,7 +629,7 @@ XPCWrappedNative::SweepTearOffs()
// If this tearoff does not have a live dedicated JSObject,
// then let's recycle it.
if (!to->GetJSObject()) {
if (!to->GetJSObjectPreserveColor()) {
nsISupports* obj = to->GetNative();
if (obj) {
obj->Release();

View File

@ -171,7 +171,7 @@ XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb)
for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
XPCWrappedNativeTearOff* to = chunk->mTearOffs;
for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
JSObject* jso = to->GetJSObject();
JSObject* jso = to->GetJSObjectPreserveColor();
if (!jso) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
cb.NoteXPCOMChild(to->GetNative());
@ -1259,7 +1259,7 @@ XPCWrappedNative::FlatJSObjectFinalized()
for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
XPCWrappedNativeTearOff* to = chunk->mTearOffs;
for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
JSObject* jso = to->GetJSObject();
JSObject* jso = to->GetJSObjectPreserveColor();
if (jso) {
NS_ASSERTION(JS_IsAboutToBeFinalized(jso), "bad!");
JS_SetPrivate(jso, nsnull);
@ -1363,8 +1363,8 @@ XPCWrappedNative::SystemIsBeingShutDown()
for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
XPCWrappedNativeTearOff* to = chunk->mTearOffs;
for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
if (to->GetJSObject()) {
JS_SetPrivate(to->GetJSObject(), nsnull);
if (JSObject *jso = to->GetJSObjectPreserveColor()) {
JS_SetPrivate(jso, nsnull);
to->SetJSObject(nsnull);
}
// We leak the tearoff mNative
@ -1777,7 +1777,7 @@ XPCWrappedNative::FindTearOff(XPCCallContext& ccx,
to < end;
to++) {
if (to->GetInterface() == aInterface) {
if (needJSObject && !to->GetJSObject()) {
if (needJSObject && !to->GetJSObjectPreserveColor()) {
AutoMarkingWrappedNativeTearOffPtr tearoff(ccx, to);
JSBool ok = InitTearOffJSObject(ccx, to);
// During shutdown, we don't sweep tearoffs. So make sure

View File

@ -327,6 +327,8 @@ XPCWrappedNativeScope::GetPrototypeNoHelper(XPCCallContext& ccx)
NS_ASSERTION(mPrototypeNoHelper,
"Failed to create prototype for wrappers w/o a helper");
} else {
xpc_UnmarkGrayObject(mPrototypeNoHelper);
}
return mPrototypeNoHelper;

View File

@ -491,8 +491,10 @@ ListBase<LC>::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope)
JSObject *interfacePrototype;
if (cache.IsInitialized()) {
if (cache.Get(sInterfaceClass.name, &interfacePrototype))
if (cache.Get(sInterfaceClass.name, &interfacePrototype)) {
xpc_UnmarkGrayObject(interfacePrototype);
return interfacePrototype;
}
} else if (!cache.Init()) {
return NULL;
}

View File

@ -2382,7 +2382,8 @@ public:
XPCNativeInterface* GetInterface() const {return mInterface;}
nsISupports* GetNative() const {return mNative;}
JSObject* GetJSObject() const;
JSObject* GetJSObject();
JSObject* GetJSObjectPreserveColor() const;
void SetInterface(XPCNativeInterface* Interface) {mInterface = Interface;}
void SetNative(nsISupports* Native) {mNative = Native;}
void SetJSObject(JSObject* JSObj);

View File

@ -103,8 +103,10 @@ WrapperFactory::WaiveXray(JSContext *cx, JSObject *obj)
CompartmentPrivate *priv =
(CompartmentPrivate *)JS_GetCompartmentPrivate(cx, js::GetObjectCompartment(obj));
JSObject *wobj = nsnull;
if (priv && priv->waiverWrapperMap)
if (priv && priv->waiverWrapperMap) {
wobj = priv->waiverWrapperMap->Find(obj);
xpc_UnmarkGrayObject(wobj);
}
// No wrapper yet, make one.
if (!wobj) {

View File

@ -16,6 +16,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=36619
<input id='a' type='file'>
</div>
<button id='b' onclick="document.getElementById('a').click();">Show Filepicker</button>
<button id='c' onmousedown="document.getElementById('a').click();">Show Filepicker</button>
<button id='d' onmouseup="document.getElementById('a').click();">Show Filepicker</button>
<pre id="test">
<script type="application/javascript">
@ -36,7 +38,7 @@ SpecialPowers.pushPrefEnv({'set': [
// Tests that a click on 'b' calls the show method.
var b = document.getElementById('b');
b.focus(); // Be sure the element is visible.
synthesizeMouse(b, 2, 2, {});
synthesizeMouseAtCenter(b, {});
SimpleTest.executeSoon(function() {
ok(MockFilePicker.shown, "File picker show method should have been called");
MockFilePicker.reset();
@ -45,12 +47,24 @@ SpecialPowers.pushPrefEnv({'set': [
document.getElementById("a").click();
SimpleTest.executeSoon(function() {
ok(!MockFilePicker.shown, "File picker show method should not have been called");
MockFilePicker.cleanup();
MockFilePicker.reset();
synthesizeMouseAtCenter(document.getElementById('c'), {});
SimpleTest.executeSoon(function() {
ok(!MockFilePicker.shown, "File picker show method should have been called");
MockFilePicker.reset();
synthesizeMouseAtCenter(document.getElementById('d'), {});
SimpleTest.executeSoon(function() {
ok(MockFilePicker.shown, "File picker show method should have been called");
MockFilePicker.cleanup();
SimpleTest.finish();
});
});
});
});
});
});
</script>

View File

@ -1236,6 +1236,7 @@ nsCSSValue::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
case eCSSUnit_Pixel:
case eCSSUnit_Degree:
case eCSSUnit_Grad:
case eCSSUnit_Turn:
case eCSSUnit_Radian:
case eCSSUnit_Hertz:
case eCSSUnit_Kilohertz:

View File

@ -7,6 +7,7 @@
[testFlingCorrectness]
[testOverscroll]
[testAxisLocking]
[testAboutPage]
# Used for Talos, please don't use in mochitest
#[testPan]

View File

@ -0,0 +1,47 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*;
import android.app.Activity;
import android.util.Log;
public class testAboutPage extends BaseTest {
public void testAboutPage() {
setTestType("mochitest");
mActions.expectGeckoEvent("Gecko:Ready").blockForEvent();
// Load the about: page
String url = "about:";
loadUrl(url);
Element awesomebar = mDriver.findElement(getActivity(), "awesome_bar");
mAsserter.ok(awesomebar.getText().matches("About (Fennec|Nightly|Aurora|Firefox)"), "page title match", "about: page title is correct");
// Open a new page to remove the about: page from the current tab
url = getAbsoluteUrl("/robocop/robocop_blank_01.html");
loadUrl(url);
// Use the menu to open the Settings
mActions.sendSpecialKey(Actions.SpecialKey.MENU);
// Look for the 'More' menu if this device/OS uses it
if (mSolo.waitForText("^More$")) {
mSolo.clickOnText("^More$");
}
mSolo.waitForText("^Settings$");
mSolo.clickOnText("^Settings$");
// Tap on the "About Xxxx" setting
mSolo.waitForText("About (Fennec|Nightly|Aurora|Firefox)");
mSolo.clickOnText("About (Fennec|Nightly|Aurora|Firefox)");
// Wait for the new tab and page to load
mActions.expectGeckoEvent("Tab:Added").blockForEvent();
mActions.expectGeckoEvent("DOMContentLoaded").blockForEvent();
// Grab the title to make sure the about: page was loaded
awesomebar = mDriver.findElement(getActivity(), "awesome_bar");
mAsserter.ok(awesomebar.getText().matches("About (Fennec|Nightly|Aurora|Firefox)"), "page title match", "about: page title is correct");
}
}

View File

@ -416,7 +416,7 @@ ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
GetLastError()));
}
}
LocalFree(argv);
return result;
}

View File

@ -15,8 +15,8 @@ Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
const PATH = "/submit/telemetry/test-ping";
const SERVER = "http://localhost:4444";
const IGNORE_HISTOGRAM = "test::ignore_me";
const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED"
const IGNORE_CLONED_HISTOGRAM = "test::ignore_me_also"
const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED";
const IGNORE_CLONED_HISTOGRAM = "test::ignore_me_also";
const BinaryInputStream = Components.Constructor(
"@mozilla.org/binaryinputstream;1",
@ -61,7 +61,7 @@ function checkHistograms(request, response) {
.decodeFromStream(s, s.available());
do_check_eq(request.getHeader("content-type"), "application/json; charset=UTF-8");
do_check_true(payload.simpleMeasurements.uptime >= 0)
do_check_true(payload.simpleMeasurements.uptime >= 0);
do_check_true(payload.simpleMeasurements.startupInterrupted === 1);
// get rid of the non-deterministic field
const expected_info = {
@ -78,6 +78,10 @@ function checkHistograms(request, response) {
do_check_eq(payload.info[f], expected_info[f]);
}
try {
// If we've not got nsIGfxInfoDebug, then this will throw and stop us doing
// this test.
var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
var isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
var isOSX = ("nsILocalFileMac" in Components.interfaces);
@ -85,6 +89,9 @@ function checkHistograms(request, response) {
do_check_true("adapterVendorID" in payload.info);
do_check_true("adapterDeviceID" in payload.info);
}
}
catch (x) {
}
const TELEMETRY_PING = "TELEMETRY_PING";
const TELEMETRY_SUCCESS = "TELEMETRY_SUCCESS";
@ -105,8 +112,8 @@ function checkHistograms(request, response) {
histogram_type: 2,
values: {0:1, 1:1, 2:0},
sum: 1
}
let tc = payload.histograms[TELEMETRY_SUCCESS]
};
let tc = payload.histograms[TELEMETRY_SUCCESS];
do_check_eq(uneval(tc),
uneval(expected_tc));
@ -176,6 +183,14 @@ function dummyTheme(id) {
}
function run_test() {
try {
var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
gfxInfo.spoofVendorID("0xabcd");
gfxInfo.spoofDeviceID("0x1234");
} catch (x) {
// If we can't test gfxInfo, that's fine, we'll note it later.
}
// Addon manager needs a profile directory
do_get_profile();
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
@ -192,5 +207,5 @@ function run_test() {
// spin the event loop
do_test_pending();
// ensure that test runs to completion
do_register_cleanup(function () do_check_true(gFinished))
do_register_cleanup(function () do_check_true(gFinished));
}

View File

@ -85,16 +85,12 @@ CPPSRCS += \
$(NULL)
endif
ifeq ($(OS_TARGET),Darwin)
# For now we use platform-linux.cc because we can't unwind
# another thread on mac using backtrace(), the implementation
# for platform-macosx.cc is in the hg history and should be
# used when we can stackwalk using a thread handle.
DEFINES += -DMOZ_ENABLE_PROFILER_SPS
CPPSRCS += \
shared-libraries-macos.cc \
platform-linux.cc \
platform-macos.cc \
TableTicker.cpp \
$(NULL)
endif

View File

@ -45,9 +45,10 @@
#include "prenv.h"
#include "shared-libraries.h"
#include "mozilla/StringBuilder.h"
#include "mozilla/StackWalk.h"
// we eventually want to make this runtime switchable
#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_UNIX))
#if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX))
#ifndef ANDROID
#define USE_BACKTRACE
#endif
@ -56,7 +57,7 @@
#include <execinfo.h>
#endif
#if defined(MOZ_PROFILING) && defined(XP_WIN)
#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
#define USE_NS_STACKWALK
#endif
#ifdef USE_NS_STACKWALK
@ -353,7 +354,7 @@ class TableTicker: public Sampler {
private:
// Not implemented on platforms which do not support backtracing
void doBacktrace(Profile &aProfile);
void doBacktrace(Profile &aProfile, Address pc);
private:
Profile mProfile;
@ -419,8 +420,9 @@ void TableTicker::HandleSaveRequest()
NS_DispatchToMainThread(runnable);
}
#ifdef USE_BACKTRACE
void TableTicker::doBacktrace(Profile &aProfile)
void TableTicker::doBacktrace(Profile &aProfile, Address pc)
{
void *array[100];
int count = backtrace (array, 100);
@ -434,6 +436,7 @@ void TableTicker::doBacktrace(Profile &aProfile)
}
#endif
#ifdef USE_NS_STACKWALK
typedef struct {
void** array;
@ -452,17 +455,23 @@ void StackWalkCallback(void* aPC, void* aClosure)
array->array[array->count++] = aPC;
}
void TableTicker::doBacktrace(Profile &aProfile)
void TableTicker::doBacktrace(Profile &aProfile, Address fp)
{
#ifndef XP_MACOSX
uintptr_t thread = GetThreadHandle(platform_data());
MOZ_ASSERT(thread);
#endif
void* pc_array[1000];
PCArray array = {
pc_array,
mozilla::ArrayLength(pc_array),
0
};
#ifdef XP_MACOSX
nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast<void**>(fp));
#else
nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
#endif
if (NS_SUCCEEDED(rv)) {
aProfile.addTag(ProfileEntry('s', "(root)", 0));
@ -539,7 +548,7 @@ void TableTicker::Tick(TickSample* sample)
#if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
if (mUseStackWalk) {
doBacktrace(mProfile);
doBacktrace(mProfile, sample->fp);
} else {
doSampleStackTrace(mStack, mProfile, sample);
}

View File

@ -0,0 +1,309 @@
#include <dlfcn.h>
#include <unistd.h>
#include <sys/mman.h>
#include <mach/mach_init.h>
#include <mach-o/dyld.h>
#include <mach-o/getsect.h>
#include <AvailabilityMacros.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <libkern/OSAtomic.h>
#include <mach/mach.h>
#include <mach/semaphore.h>
#include <mach/task.h>
#include <mach/vm_statistics.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "platform.h"
// this port is based off of v8 svn revision 9837
// XXX: this is a very stubbed out implementation
// that only supports a single Sampler
struct SamplerRegistry {
static void AddActiveSampler(Sampler *sampler) {
ASSERT(!SamplerRegistry::sampler);
SamplerRegistry::sampler = sampler;
}
static void RemoveActiveSampler(Sampler *sampler) {
SamplerRegistry::sampler = NULL;
}
static Sampler *sampler;
};
Sampler *SamplerRegistry::sampler = NULL;
// 0 is never a valid thread id on MacOSX since a ptread_t is
// a pointer.
static const pthread_t kNoThread = (pthread_t) 0;
class MacOSMutex : public Mutex {
public:
MacOSMutex() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex_, &attr);
}
virtual ~MacOSMutex() { pthread_mutex_destroy(&mutex_); }
virtual int Lock() { return pthread_mutex_lock(&mutex_); }
virtual int Unlock() { return pthread_mutex_unlock(&mutex_); }
virtual bool TryLock() {
int result = pthread_mutex_trylock(&mutex_);
// Return false if the lock is busy and locking failed.
if (result == EBUSY) {
return false;
}
ASSERT(result == 0); // Verify no other errors.
return true;
}
private:
pthread_mutex_t mutex_;
};
Mutex* OS::CreateMutex() {
return new MacOSMutex();
}
void OS::Sleep(int milliseconds) {
usleep(1000 * milliseconds);
}
class Thread::PlatformData : public Malloced {
public:
PlatformData() : thread_(kNoThread) {}
pthread_t thread_; // Thread handle for pthread.
};
Thread::Thread(const char* name)
: data_(new PlatformData),
stack_size_(0) {
set_name(name);
}
Thread::~Thread() {
delete data_;
}
static void SetThreadName(const char* name) {
// pthread_setname_np is only available in 10.6 or later, so test
// for it at runtime.
int (*dynamic_pthread_setname_np)(const char*);
*reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
dlsym(RTLD_DEFAULT, "pthread_setname_np");
if (!dynamic_pthread_setname_np)
return;
// Mac OS X does not expose the length limit of the name, so hardcode it.
static const int kMaxNameLength = 63;
USE(kMaxNameLength);
ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
dynamic_pthread_setname_np(name);
}
static void* ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(arg);
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
thread->data()->thread_ = pthread_self();
SetThreadName(thread->name());
ASSERT(thread->data()->thread_ != kNoThread);
thread->Run();
return NULL;
}
void Thread::set_name(const char* name) {
strncpy(name_, name, sizeof(name_));
name_[sizeof(name_) - 1] = '\0';
}
void Thread::Start() {
pthread_attr_t* attr_ptr = NULL;
pthread_attr_t attr;
if (stack_size_ > 0) {
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
attr_ptr = &attr;
}
pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
ASSERT(data_->thread_ != kNoThread);
}
void Thread::Join() {
pthread_join(data_->thread_, NULL);
}
class Sampler::PlatformData : public Malloced {
public:
PlatformData() : profiled_thread_(mach_thread_self()) {}
~PlatformData() {
// Deallocate Mach port for thread.
mach_port_deallocate(mach_task_self(), profiled_thread_);
}
thread_act_t profiled_thread() { return profiled_thread_; }
private:
// Note: for profiled_thread_ Mach primitives are used instead of PThread's
// because the latter doesn't provide thread manipulation primitives required.
// For details, consult "Mac OS X Internals" book, Section 7.3.
thread_act_t profiled_thread_;
};
class SamplerThread : public Thread {
public:
explicit SamplerThread(int interval)
: Thread("SamplerThread"),
interval_(interval) {}
static void AddActiveSampler(Sampler* sampler) {
ScopedLock lock(mutex_);
SamplerRegistry::AddActiveSampler(sampler);
if (instance_ == NULL) {
instance_ = new SamplerThread(sampler->interval());
instance_->Start();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
}
static void RemoveActiveSampler(Sampler* sampler) {
ScopedLock lock(mutex_);
instance_->Join();
//XXX: unlike v8 we need to remove the active sampler after doing the Join
// because we drop the sampler immediately
SamplerRegistry::RemoveActiveSampler(sampler);
delete instance_;
instance_ = NULL;
/*
if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
delete instance_;
instance_ = NULL;
}
*/
}
// Implement Thread::Run().
virtual void Run() {
while (SamplerRegistry::sampler->IsActive()) {
SampleContext(SamplerRegistry::sampler);
OS::Sleep(interval_);
}
}
void SampleContext(Sampler* sampler) {
thread_act_t profiled_thread = sampler->platform_data()->profiled_thread();
TickSample sample_obj;
TickSample* sample = &sample_obj;
//TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate());
//if (sample == NULL) sample = &sample_obj;
if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
#if V8_HOST_ARCH_X64
thread_state_flavor_t flavor = x86_THREAD_STATE64;
x86_thread_state64_t state;
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
#if __DARWIN_UNIX03
#define REGISTER_FIELD(name) __r ## name
#else
#define REGISTER_FIELD(name) r ## name
#endif // __DARWIN_UNIX03
#elif V8_HOST_ARCH_IA32
thread_state_flavor_t flavor = i386_THREAD_STATE;
i386_thread_state_t state;
mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
#if __DARWIN_UNIX03
#define REGISTER_FIELD(name) __e ## name
#else
#define REGISTER_FIELD(name) e ## name
#endif // __DARWIN_UNIX03
#else
#error Unsupported Mac OS X host architecture.
#endif // V8_HOST_ARCH
if (thread_get_state(profiled_thread,
flavor,
reinterpret_cast<natural_t*>(&state),
&count) == KERN_SUCCESS) {
//sample->state = sampler->isolate()->current_vm_state();
sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
sample->timestamp = mozilla::TimeStamp::Now();
sampler->SampleStack(sample);
sampler->Tick(sample);
}
thread_resume(profiled_thread);
}
const int interval_;
//RuntimeProfilerRateLimiter rate_limiter_;
// Protects the process wide state below.
static Mutex* mutex_;
static SamplerThread* instance_;
DISALLOW_COPY_AND_ASSIGN(SamplerThread);
};
#undef REGISTER_FIELD
Mutex* SamplerThread::mutex_ = OS::CreateMutex();
SamplerThread* SamplerThread::instance_ = NULL;
Sampler::Sampler(int interval, bool profiling)
: // isolate_(isolate),
interval_(interval),
profiling_(profiling),
active_(false) /*,
samples_taken_(0)*/ {
data_ = new PlatformData;
}
Sampler::~Sampler() {
ASSERT(!IsActive());
delete data_;
}
void Sampler::Start() {
ASSERT(!IsActive());
SetActive(true);
SamplerThread::AddActiveSampler(this);
}
void Sampler::Stop() {
ASSERT(IsActive());
SetActive(false);
SamplerThread::RemoveActiveSampler(this);
}

View File

@ -530,6 +530,8 @@ PuppetWidget::DispatchPaintEvent()
DispatchEvent(&event, status);
} else {
nsRefPtr<gfxContext> ctx = new gfxContext(mSurface);
ctx->Rectangle(gfxRect(0,0,0,0));
ctx->Clip();
AutoLayerManagerSetup setupLayerManager(this, ctx,
BasicLayerManager::BUFFER_NONE);
DispatchEvent(&event, status);

View File

@ -103,6 +103,7 @@ EXPORTS_mozilla = \
MapsMemoryReporter.h \
ClearOnShutdown.h \
AvailableMemoryTracker.h \
StackWalk.h \
$(NULL)
ifeq (windows,$(MOZ_WIDGET_TOOLKIT))

52
xpcom/base/StackWalk.h Normal file
View File

@ -0,0 +1,52 @@
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jeff Muizelaar <jmuizelaar@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* API for getting a stack trace of the C/C++ */
#ifndef StackWalk_h_
#define StackWalk_h_
// XXX: it would be nice to eventually remove this header dependency on nsStackWalk.h
#include "nsStackWalk.h"
namespace mozilla {
nsresult
FramePointerStackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
void *aClosure, void **bp);
}
#endif /* !defined(StackWalk_h_) */

View File

@ -141,10 +141,8 @@
#include "prenv.h"
#include "prprf.h"
#include "plstr.h"
#include "prtime.h"
#include "nsPrintfCString.h"
#include "nsTArray.h"
#include "mozilla/FunctionTimer.h"
#include "nsIObserverService.h"
#include "nsIConsoleService.h"
#include "nsServiceManagerUtils.h"
@ -312,6 +310,36 @@ static bool nsCycleCollector_shouldSuppress(nsISupports *s);
static void InitMemHook(void);
#endif
#ifdef COLLECT_TIME_DEBUG
class TimeLog
{
public:
TimeLog() : mLastCheckpoint(TimeStamp::Now()) {}
void
Checkpoint(const char* aEvent)
{
TimeStamp now = TimeStamp::Now();
PRUint32 dur = (PRUint32) ((now - mLastCheckpoint).ToMilliseconds());
if (dur > 0) {
printf("cc: %s took %dms\n", aEvent, dur);
}
mLastCheckpoint = now;
}
private:
TimeStamp mLastCheckpoint;
};
#else
class TimeLog
{
public:
TimeLog() {}
void Checkpoint(const char* aEvent) {}
};
#endif
////////////////////////////////////////////////////////////////////////
// Base types
////////////////////////////////////////////////////////////////////////
@ -2197,6 +2225,7 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
// - Unroot(whites), which returns the whites to normal GC.
nsresult rv;
TimeLog timeLog;
NS_ASSERTION(mWhiteNodes->IsEmpty(),
"FinishCollection wasn't called?");
@ -2215,9 +2244,11 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
}
}
}
timeLog.Checkpoint("CollectWhite::Root");
if (mBeforeUnlinkCB) {
mBeforeUnlinkCB();
timeLog.Checkpoint("CollectWhite::BeforeUnlinkCB");
}
#if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
struct _CrtMemState ms1, ms2;
@ -2249,6 +2280,7 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
#endif
}
}
timeLog.Checkpoint("CollectWhite::Unlink");
for (i = 0; i < count; ++i) {
PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
@ -2256,6 +2288,7 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
if (NS_FAILED(rv))
Fault("Failed unroot call while unlinking", pinfo);
}
timeLog.Checkpoint("CollectWhite::Unroot");
#if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
_CrtMemCheckpoint(&ms2);
@ -2842,16 +2875,13 @@ nsCycleCollector::GCIfNeeded(bool aForceGC)
return;
}
#ifdef COLLECT_TIME_DEBUG
PRTime start = PR_Now();
#endif
TimeLog timeLog;
// rt->Collect() must be called from the main thread,
// because it invokes XPCJSRuntime::GCCallback(cx, JSGC_BEGIN)
// which returns false if not in the main thread.
rt->Collect(js::gcreason::CC_FORCED, nsGCNormal);
#ifdef COLLECT_TIME_DEBUG
printf("cc: GC() took %lldms\n", (PR_Now() - start) / PR_USEC_PER_MSEC);
#endif
timeLog.Checkpoint("GC()");
}
bool
@ -2866,11 +2896,8 @@ nsCycleCollector::PrepareForCollection(nsTArray<PtrInfo*> *aWhiteNodes)
if (mCollectionInProgress)
return false;
NS_TIME_FUNCTION;
TimeLog timeLog;
#ifdef COLLECT_TIME_DEBUG
printf("cc: nsCycleCollector::PrepareForCollection()\n");
#endif
mCollectionStart = TimeStamp::Now();
mVisitedRefCounted = 0;
mVisitedGCed = 0;
@ -2887,6 +2914,8 @@ nsCycleCollector::PrepareForCollection(nsTArray<PtrInfo*> *aWhiteNodes)
mWhiteNodes = aWhiteNodes;
timeLog.Checkpoint("PrepareForCollection()");
return true;
}
@ -2903,9 +2932,12 @@ nsCycleCollector::CleanupAfterCollection()
_heapmin();
#endif
PRUint32 interval((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
PRUint32 interval = (PRUint32) ((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
#ifdef COLLECT_TIME_DEBUG
printf("cc: CleanupAfterCollection(), total time %ums\n", interval);
printf("cc: total cycle collector time was %ums\n", interval);
printf("cc: visited %u ref counted and %u GCed objects, freed %d.\n",
mVisitedRefCounted, mVisitedGCed, mWhiteNodeCount);
printf("cc: \n");
#endif
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR, interval);
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted);
@ -2948,6 +2980,8 @@ bool
nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
{
// aListener should be Begin()'d before this
TimeLog timeLog;
if (mParams.mDoNothing)
return false;
@ -2955,20 +2989,12 @@ nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
if (!builder.Initialized())
return false;
#ifdef COLLECT_TIME_DEBUG
PRTime now = PR_Now();
#endif
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i])
mRuntimes[i]->BeginCycleCollection(builder, false);
}
#ifdef COLLECT_TIME_DEBUG
printf("cc: mRuntimes[*]->BeginCycleCollection() took %lldms\n",
(PR_Now() - now) / PR_USEC_PER_MSEC);
now = PR_Now();
#endif
timeLog.Checkpoint("mRuntimes[*]->BeginCycleCollection()");
#ifdef DEBUG_CC
PRUint32 purpleStart = builder.Count();
@ -2999,35 +3025,16 @@ nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
}
#endif
#ifdef COLLECT_TIME_DEBUG
printf("cc: SelectPurple() took %lldms\n",
(PR_Now() - now) / PR_USEC_PER_MSEC);
#endif
timeLog.Checkpoint("SelectPurple()");
if (builder.Count() > 0) {
// The main Bacon & Rajan collection algorithm.
#ifdef COLLECT_TIME_DEBUG
now = PR_Now();
#endif
MarkRoots(builder);
#ifdef COLLECT_TIME_DEBUG
{
PRTime then = PR_Now();
printf("cc: MarkRoots() took %lldms\n",
(then - now) / PR_USEC_PER_MSEC);
now = then;
}
#endif
timeLog.Checkpoint("MarkRoots()");
ScanRoots();
#ifdef COLLECT_TIME_DEBUG
printf("cc: ScanRoots() took %lldms\n",
(PR_Now() - now) / PR_USEC_PER_MSEC);
#endif
timeLog.Checkpoint("ScanRoots()");
mScanInProgress = false;
@ -3069,6 +3076,7 @@ nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
if (mRuntimes[i])
mRuntimes[i]->FinishTraverse();
}
timeLog.Checkpoint("mRuntimes[*]->FinishTraverse()");
}
else {
mScanInProgress = false;
@ -3080,16 +3088,9 @@ nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
bool
nsCycleCollector::FinishCollection(nsICycleCollectorListener *aListener)
{
#ifdef COLLECT_TIME_DEBUG
PRTime now = PR_Now();
#endif
TimeLog timeLog;
bool collected = CollectWhite(aListener);
#ifdef COLLECT_TIME_DEBUG
printf("cc: CollectWhite() took %lldms\n",
(PR_Now() - now) / PR_USEC_PER_MSEC);
#endif
timeLog.Checkpoint("CollectWhite()");
#ifdef DEBUG_CC
mStats.mCollection++;
@ -3101,6 +3102,7 @@ nsCycleCollector::FinishCollection(nsICycleCollectorListener *aListener)
if (mRuntimes[i])
mRuntimes[i]->FinishCycleCollection();
}
timeLog.Checkpoint("mRuntimes[*]->FinishCycleCollection()");
mFollowupCollection = true;
@ -3125,6 +3127,7 @@ nsCycleCollector::FinishCollection(nsICycleCollectorListener *aListener)
mWhiteNodes->Clear();
ClearGraph();
timeLog.Checkpoint("ClearGraph()");
mParams.mDoNothing = false;
@ -3865,7 +3868,9 @@ nsCycleCollector_forgetSkippable()
{
if (sCollector) {
SAMPLE_LABEL("CC", "nsCycleCollector_forgetSkippable");
TimeLog timeLog;
sCollector->ForgetSkippable();
timeLog.Checkpoint("ForgetSkippable()");
}
}

View File

@ -41,11 +41,14 @@
/* API for getting a stack trace of the C/C++ stack on the current thread */
#include "mozilla/Util.h"
#include "mozilla/StackWalk.h"
#include "nsDebug.h"
#include "nsStackWalkPrivate.h"
#include "nsStackWalk.h"
using namespace mozilla;
// The presence of this address is the stack must stop the stack walk. If
// there is no such address, the structure will be {NULL, true}.
struct CriticalAddress {
@ -215,8 +218,6 @@ StackWalkInitCriticalAddress()
#endif
#endif
using namespace mozilla;
// Define these as static pointers so that we can load the DLL on the
// fly (and not introduce a link-time dependency on it). Tip o' the
// hat to Matt Pietrick for this idea. See:
@ -1572,9 +1573,6 @@ NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
#else // not __sun-specific
#define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
#if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
#if __GLIBC__ > 2 || __GLIBC_MINOR > 1
#define HAVE___LIBC_STACK_END 1
#else
@ -1584,26 +1582,13 @@ NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
#if HAVE___LIBC_STACK_END
extern void *__libc_stack_end; // from ld-linux.so
#endif
EXPORT_XPCOM_API(nsresult)
NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
void *aClosure, uintptr_t aThread)
namespace mozilla {
nsresult
FramePointerStackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
void *aClosure, void **bp)
{
MOZ_ASSERT(gCriticalAddress.mInit);
MOZ_ASSERT(!aThread);
// Stack walking code courtesy Kipp's "leaky".
// Get the frame pointer
void **bp;
#if defined(__i386)
__asm__( "movl %%ebp, %0" : "=g"(bp));
#else
// It would be nice if this worked uniformly, but at least on i386 and
// x86_64, it stopped working with gcc 4.1, because it points to the
// end of the saved registers instead of the start.
bp = (void**) __builtin_frame_address(0);
#endif
int skip = aSkipFrames;
while (1) {
void **next = (void**)*bp;
@ -1636,6 +1621,33 @@ NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
return NS_OK;
}
}
#define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
#if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
EXPORT_XPCOM_API(nsresult)
NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
void *aClosure, uintptr_t aThread)
{
MOZ_ASSERT(gCriticalAddress.mInit);
MOZ_ASSERT(!aThread);
// Get the frame pointer
void **bp;
#if defined(__i386)
__asm__( "movl %%ebp, %0" : "=g"(bp));
#else
// It would be nice if this worked uniformly, but at least on i386 and
// x86_64, it stopped working with gcc 4.1, because it points to the
// end of the saved registers instead of the start.
bp = (void**) __builtin_frame_address(0);
#endif
return FramePointerStackWalk(aCallback, aSkipFrames,
aClosure, bp);
}
#elif defined(HAVE__UNWIND_BACKTRACE)
// libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0