Merge m-i to m-c

This commit is contained in:
Phil Ringnalda 2013-11-23 08:18:35 -08:00
commit 9710fc9cf4
77 changed files with 1977 additions and 332 deletions

View File

@ -62,7 +62,7 @@ namespace mozilla {
namespace dom {
// The default value for kKindTable is "subtitles"
static const char* kKindTableDefaultString = kKindTable->tag;
static MOZ_CONSTEXPR const char* kKindTableDefaultString = kKindTable->tag;
/** HTMLTrackElement */
HTMLTrackElement::HTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo)

View File

@ -22,7 +22,7 @@ namespace mozilla {
namespace dom {
// Map html attribute string values to TextTrackKind enums.
static const nsAttrValue::EnumTable kKindTable[] = {
static MOZ_CONSTEXPR nsAttrValue::EnumTable kKindTable[] = {
{ "subtitles", static_cast<int16_t>(TextTrackKind::Subtitles) },
{ "captions", static_cast<int16_t>(TextTrackKind::Captions) },
{ "descriptions", static_cast<int16_t>(TextTrackKind::Descriptions) },

View File

@ -36,7 +36,10 @@ var ecmaGlobals =
"Float32Array",
"Float64Array",
"Function",
"Infinity",
// NB: We haven't bothered to resolve constants like Infinity and NaN on
// Xrayed windows (which are seen from the XBL scope). We could support
// this if needed with some refactoring.
{name: "Infinity", xbl: false},
"Int16Array",
"Int32Array",
"Int8Array",
@ -46,7 +49,7 @@ var ecmaGlobals =
"JSON",
"Map",
"Math",
"NaN",
{name: "NaN", xbl: false},
"Number",
"Object",
{name: "ParallelArray", nightly: true},
@ -657,9 +660,7 @@ function createInterfaceMap(isXBLScope) {
}
}
// Standard ECMAScript global objects are not defined on the XBL scope, but
// everything else exists everywhere.
addInterfaces(ecmaGlobals, !isXBLScope);
addInterfaces(ecmaGlobals, true);
addInterfaces(interfaceNamesInGlobalScope, true);
return interfaceMap;

View File

@ -5260,23 +5260,16 @@ WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
return false;
}
nsRefPtr<nsDOMMessageEvent> event;
{
// Bug 940779 - MessageEventInit contains unrooted JS objects, and
// ~nsRefPtr can GC, so make sure 'init' is no longer live before ~nsRefPtr
// runs (or the nsRefPtr is even created) to avoid a rooting hazard. Note
// that 'init' is live until its destructor runs, not just until its final
// use.
MessageEventInit init;
init.mBubbles = false;
init.mCancelable = false;
init.mSource = &jsPort.toObject();
RootedDictionary<MessageEventInit> init(aCx);
init.mBubbles = false;
init.mCancelable = false;
init.mSource = &jsPort.toObject();
ErrorResult rv;
event = nsDOMMessageEvent::Constructor(globalObject, aCx,
NS_LITERAL_STRING("connect"),
init, rv);
}
ErrorResult rv;
nsRefPtr<nsDOMMessageEvent> event =
nsDOMMessageEvent::Constructor(globalObject, aCx,
NS_LITERAL_STRING("connect"), init, rv);
event->SetTrusted(true);

View File

@ -1965,8 +1965,12 @@ XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
void
XMLHttpRequest::Send(JSObject* aBody, ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
JSContext* cx = mWorkerPrivate->GetJSContext();
MOZ_ASSERT(aBody);
JS::Rooted<JSObject*> body(cx, aBody);
mWorkerPrivate->AssertIsOnWorkerThread();
if (mCanceled) {
aRv.Throw(UNCATCHABLE_EXCEPTION);
@ -1978,15 +1982,13 @@ XMLHttpRequest::Send(JSObject* aBody, ErrorResult& aRv)
return;
}
JSContext* cx = mWorkerPrivate->GetJSContext();
JS::Rooted<JS::Value> valToClone(cx);
if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
file::GetDOMBlobFromJSObject(aBody)) {
valToClone.setObject(*aBody);
if (JS_IsArrayBufferObject(body) || JS_IsArrayBufferViewObject(body) ||
file::GetDOMBlobFromJSObject(body)) {
valToClone.setObject(*body);
}
else {
JS::Rooted<JS::Value> obj(cx, JS::ObjectValue(*aBody));
JS::Rooted<JS::Value> obj(cx, JS::ObjectValue(*body));
JSString* bodyStr = JS::ToString(cx, obj);
if (!bodyStr) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);

View File

@ -4,10 +4,14 @@
# 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/.
SOURCES += [
UNIFIED_SOURCES += [
'nsAuthFactory.cpp',
'nsAuthGSSAPI.cpp',
'nsAuthSASL.cpp',
]
# contains constants whose names conflict with constants in other files
SOURCES += [
'nsHttpNegotiateAuth.cpp',
]
@ -17,7 +21,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
]
DEFINES['USE_SSPI'] = True
else:
SOURCES += [
UNIFIED_SOURCES += [
'nsAuthSambaNTLM.cpp',
]

View File

@ -13,7 +13,7 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'cookie'
SOURCES += [
UNIFIED_SOURCES += [
'nsCookieModule.cpp',
'nsCookiePermission.cpp',
'nsCookiePromptService.cpp',

View File

@ -4,7 +4,7 @@
# 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/.
SOURCES += [
UNIFIED_SOURCES += [
'nsContentBlocker.cpp',
'nsModuleFactory.cpp',
]

View File

@ -4,7 +4,7 @@
# 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/.
SOURCES += [
UNIFIED_SOURCES += [
'nsAutoConfig.cpp',
'nsConfigFactory.cpp',
'nsJSConfigTriggers.cpp',

View File

@ -3,6 +3,9 @@
* 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 nsAutoConfig_h
#define nsAutoConfig_h
#include "nsIAutoConfig.h"
#include "nsITimer.h"
#include "nsIFile.h"
@ -46,3 +49,5 @@ class nsAutoConfig : public nsIAutoConfig,
nsCOMPtr<nsITimer> mTimer;
nsCString mConfigURL;
};
#endif

View File

@ -3,6 +3,9 @@
* 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 nsReadConfig_h
#define nsReadConfig_h
#include "nsCOMPtr.h"
#include "nsIReadConfig.h"
#include "nsIAutoConfig.h"
@ -33,3 +36,5 @@ class nsReadConfig : public nsIReadConfig,
private:
nsCOMPtr<nsIAutoConfig> mAutoConfig;
};
#endif

View File

@ -4,7 +4,7 @@
# 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/.
SOURCES += [
UNIFIED_SOURCES += [
'CharDistribution.cpp',
'JpCntx.cpp',
'LangBulgarianModel.cpp',

View File

@ -4,7 +4,7 @@
# 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/.
SOURCES += [
UNIFIED_SOURCES += [
'nsUdetXPCOMWrapper.cpp',
'nsUniversalCharDetModule.cpp',
]

View File

@ -6,6 +6,8 @@
#ifndef MOZILLA_GFX_BASEPOINT_H_
#define MOZILLA_GFX_BASEPOINT_H_
#include "mozilla/Attributes.h"
namespace mozilla {
namespace gfx {
@ -19,8 +21,8 @@ struct BasePoint {
T x, y;
// Constructors
BasePoint() : x(0), y(0) {}
BasePoint(T aX, T aY) : x(aX), y(aY) {}
MOZ_CONSTEXPR BasePoint() : x(0), y(0) {}
MOZ_CONSTEXPR BasePoint(T aX, T aY) : x(aX), y(aY) {}
void MoveTo(T aX, T aY) { x = aX; y = aY; }
void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }

View File

@ -6,6 +6,7 @@
#ifndef MOZILLA_GFX_POINT_H_
#define MOZILLA_GFX_POINT_H_
#include "mozilla/Attributes.h"
#include "Types.h"
#include "BasePoint.h"
#include "BaseSize.h"
@ -25,7 +26,7 @@ struct IntPointTyped :
typedef BasePoint< int32_t, IntPointTyped<units> > Super;
IntPointTyped() : Super() {}
IntPointTyped(int32_t aX, int32_t aY) : Super(aX, aY) {}
MOZ_CONSTEXPR IntPointTyped(int32_t aX, int32_t aY) : Super(aX, aY) {}
// XXX When all of the code is ported, the following functions to convert to and from
// unknown types should be removed.

View File

@ -36,7 +36,7 @@ public:
// We use IDs to identify frames across processes.
typedef uint64_t ViewID;
static const ViewID NULL_SCROLL_ID; // This container layer does not scroll.
static const ViewID START_SCROLL_ID; // This is the ID that scrolling subframes
static const ViewID START_SCROLL_ID = 2; // This is the ID that scrolling subframes
// will begin at.
FrameMetrics()

View File

@ -37,7 +37,6 @@ using namespace mozilla::gfx;
typedef FrameMetrics::ViewID ViewID;
const ViewID FrameMetrics::NULL_SCROLL_ID = 0;
const ViewID FrameMetrics::START_SCROLL_ID = 1;
uint8_t gLayerManagerLayerBuilder;

View File

@ -125,7 +125,7 @@ struct gfxRGBA {
* Intialize this color using explicit red, green, blue and alpha
* values.
*/
gfxRGBA(gfxFloat _r, gfxFloat _g, gfxFloat _b, gfxFloat _a=1.0) : r(_r), g(_g), b(_b), a(_a) {}
MOZ_CONSTEXPR gfxRGBA(gfxFloat _r, gfxFloat _g, gfxFloat _b, gfxFloat _a=1.0) : r(_r), g(_g), b(_b), a(_a) {}
/**
* Initialize this color from a packed 32-bit color.

View File

@ -11,6 +11,9 @@
#include "nsDebug.h"
#include "nsTraceRefcnt.h"
// Undo the damage done by mozzconf.h
#undef compress
using namespace mozilla;
using namespace std;

View File

@ -84,8 +84,8 @@ public:
void* mem = shmat(mHandle, nullptr, 0);
if (mem == (void*) -1) {
char warning[256];
snprintf(warning, sizeof(warning)-1,
"shmat(): %s (%d)\n", strerror(errno), errno);
::snprintf(warning, sizeof(warning)-1,
"shmat(): %s (%d)\n", strerror(errno), errno);
NS_WARNING(warning);

View File

@ -44,7 +44,7 @@ else:
EXPORTS.mozilla.ipc += [
'Transport_posix.h',
]
SOURCES += [
UNIFIED_SOURCES += [
'CrossProcessMutex_unimplemented.cpp',
'SharedMemory_posix.cpp',
'Transport_posix.cpp',
@ -55,18 +55,18 @@ else:
# impl.
if CONFIG['OS_TARGET'] == 'Android':
EXPORTS.mozilla.ipc += ['SharedMemoryBasic_android.h']
SOURCES += [
UNIFIED_SOURCES += [
'SharedMemoryBasic_android.cpp',
]
else:
EXPORTS.mozilla.ipc += ['SharedMemoryBasic_chromium.h']
if CONFIG['OS_ARCH'] == 'Linux':
SOURCES += [
UNIFIED_SOURCES += [
'ProcessUtils_linux.cpp',
]
else:
SOURCES += [
UNIFIED_SOURCES += [
'ProcessUtils_none.cpp',
]
@ -74,11 +74,10 @@ EXPORTS.ipc += [
'IPCMessageUtils.h',
]
SOURCES += [
UNIFIED_SOURCES += [
'BrowserProcessSubThread.cpp',
'FileDescriptor.cpp',
'FileDescriptorUtils.cpp',
'GeckoChildProcessHost.cpp',
'InputStreamUtils.cpp',
'MessageChannel.cpp',
'MessageLink.cpp',
@ -89,6 +88,12 @@ SOURCES += [
'SharedMemory.cpp',
'Shmem.cpp',
'StringUtil.cpp',
]
# GeckoChildProcessHost.cpp cannot be built in unified mode because it uses plarena.h.
# URIUtils.cpp cannot be built in unified mode because of name clashes on strdup.
SOURCES += [
'GeckoChildProcessHost.cpp',
'URIUtils.cpp',
]

View File

@ -50,15 +50,15 @@ class ProfileEntry
// instances are volatile. These methods would not be available unless they
// were marked as volatile as well.
bool js() volatile {
bool js() const volatile {
JS_ASSERT_IF(sp == nullptr, script_ != nullptr);
return sp == nullptr;
}
uint32_t line() volatile { JS_ASSERT(!js()); return idx; }
JSScript *script() volatile { JS_ASSERT(js()); return script_; }
void *stackAddress() volatile { return sp; }
const char *label() volatile { return string; }
uint32_t line() const volatile { JS_ASSERT(!js()); return idx; }
JSScript *script() const volatile { JS_ASSERT(js()); return script_; }
void *stackAddress() const volatile { return sp; }
const char *label() const volatile { return string; }
void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; }
void setLabel(const char *aString) volatile { string = aString; }
@ -66,7 +66,7 @@ class ProfileEntry
void setScript(JSScript *aScript) volatile { script_ = aScript; }
// We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
JS_FRIEND_API(jsbytecode *) pc() volatile;
JS_FRIEND_API(jsbytecode *) pc() const volatile;
JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile;
static size_t offsetOfString() { return offsetof(ProfileEntry, string); }

View File

@ -2042,7 +2042,7 @@ js_InitIntlClass(JSContext *cx, HandleObject obj)
return nullptr;
}
global->markStandardClassInitializedNoProto(&IntlClass);
global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
return Intl;
}

View File

@ -698,12 +698,12 @@ s2b
static int
hi0bits
#ifdef KR_headers
(x) register ULong x;
(x) ULong x;
#else
(register ULong x)
(ULong x)
#endif
{
register int k = 0;
int k = 0;
if (!(x & 0xffff0000)) {
k = 16;
@ -737,8 +737,8 @@ lo0bits
(ULong *y)
#endif
{
register int k;
register ULong x = *y;
int k;
ULong x = *y;
if (x & 7) {
if (x & 1)
@ -1156,7 +1156,7 @@ ulp
(U x)
#endif
{
register Long L;
Long L;
U a;
L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;

View File

@ -55,7 +55,7 @@ FindKeyword(const jschar *s, size_t length)
{
JS_ASSERT(length != 0);
register size_t i;
size_t i;
const KeywordInfo *kw;
const char *chars;

View File

@ -1162,61 +1162,54 @@ JS_InitStandardClasses(JSContext *cx, JSObject *objArg)
#define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[ScalarTypeRepresentation::type])
#define EAGER_ATOM(name) NAME_OFFSET(name)
#define EAGER_CLASS_ATOM(name) NAME_OFFSET(name)
#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name)
#define EAGER_ATOM_AND_OCLASP(name) EAGER_CLASS_ATOM(name), OCLASP(name)
static JSObject *
DummyInit(JSContext *cx, HandleObject obj)
{
MOZ_ASSUME_UNREACHABLE();
return nullptr;
}
typedef struct JSStdName {
ClassInitializerOp init;
size_t atomOffset; /* offset of atom pointer in JSAtomState */
const Class *clasp;
bool isDummy() const { return init == DummyInit; };
} JSStdName;
static Handle<PropertyName*>
StdNameToPropertyName(JSContext *cx, const JSStdName *stdn)
static const JSStdName*
LookupStdName(JSRuntime *rt, HandleString name, const JSStdName *table)
{
return AtomStateOffsetToName(cx->runtime()->atomState, stdn->atomOffset);
MOZ_ASSERT(name->isAtom());
for (unsigned i = 0; table[i].init; i++) {
if (table[i].isDummy())
continue;
JSAtom *atom = AtomStateOffsetToName(rt->atomState, table[i].atomOffset);
MOZ_ASSERT(atom);
if (name == atom)
return &table[i];
}
return nullptr;
}
/*
* Table of class initializers and their atom offsets in rt->atomState.
* If you add a "standard" class, remember to update this table.
* Table of standard classes, indexed by JSProtoKey. For entries where the
* JSProtoKey does not correspond to a class with a meaningful constructor, we
* insert a null entry into the table.
*/
static const JSStdName standard_class_atoms[] = {
{js_InitFunctionClass, EAGER_CLASS_ATOM(Function), &JSFunction::class_},
{js_InitObjectClass, EAGER_CLASS_ATOM(Object), &JSObject::class_},
{js_InitArrayClass, EAGER_ATOM_AND_OCLASP(Array)},
{js_InitBooleanClass, EAGER_ATOM_AND_OCLASP(Boolean)},
{js_InitDateClass, EAGER_ATOM_AND_OCLASP(Date)},
{js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)},
{js_InitNumberClass, EAGER_ATOM_AND_OCLASP(Number)},
{js_InitStringClass, EAGER_ATOM_AND_OCLASP(String)},
{js_InitExceptionClasses, EAGER_ATOM_AND_OCLASP(Error)},
{js_InitRegExpClass, EAGER_ATOM_AND_OCLASP(RegExp)},
{js_InitIteratorClasses, EAGER_ATOM_AND_OCLASP(StopIteration)},
{js_InitJSONClass, EAGER_ATOM_AND_CLASP(JSON)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBufferObject::protoClass},
{js_InitWeakMapClass, EAGER_ATOM_AND_OCLASP(WeakMap)},
{js_InitMapClass, EAGER_ATOM_AND_OCLASP(Map)},
{js_InitSetClass, EAGER_ATOM_AND_OCLASP(Set)},
#ifdef ENABLE_PARALLEL_JS
{js_InitParallelArrayClass, EAGER_ATOM_AND_OCLASP(ParallelArray)},
#endif
{js_InitProxyClass, EAGER_CLASS_ATOM(Proxy), &ProxyObject::uncallableClass_},
#if EXPOSE_INTL_API
{js_InitIntlClass, EAGER_ATOM_AND_CLASP(Intl)},
#endif
#ifdef ENABLE_BINARYDATA
{js_InitTypedObjectClass, EAGER_ATOM_AND_CLASP(TypedObject)},
#endif
{nullptr, 0, nullptr}
#define STD_NAME_ENTRY(name, code, init, clasp) { init, EAGER_CLASS_ATOM(name), clasp },
#define STD_DUMMY_ENTRY(name, code, init, dummy) { DummyInit, 0, nullptr },
static const JSStdName standard_class_names[] = {
JS_FOR_PROTOTYPES(STD_NAME_ENTRY, STD_DUMMY_ENTRY)
{ nullptr, 0, nullptr }
};
/*
* Table of top-level function and constant names and their init functions.
* If you add a "standard" global function or property, remember to update
* this table.
* Table of top-level function and constant names and the init function of the
* corresponding standard class that sets them up.
*/
static const JSStdName standard_class_names[] = {
static const JSStdName builtin_property_names[] = {
{js_InitObjectClass, EAGER_ATOM(eval), &JSObject::class_},
/* Global properties and functions defined by the Number class. */
@ -1238,32 +1231,6 @@ static const JSStdName standard_class_names[] = {
{js_InitStringClass, EAGER_ATOM(uneval), OCLASP(String)},
#endif
/* Exception constructors. */
{js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), OCLASP(Error)},
{js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), OCLASP(Error)},
{js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), OCLASP(Error)},
{js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), OCLASP(Error)},
{js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), OCLASP(Error)},
{js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), OCLASP(Error)},
{js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), OCLASP(Error)},
{js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), OCLASP(Error)},
{js_InitIteratorClasses, EAGER_CLASS_ATOM(Iterator), &PropertyIteratorObject::class_},
/* Typed Arrays */
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &ArrayBufferObject::class_},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int8Array), TYPED_ARRAY_CLASP(TYPE_INT8)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8Array), TYPED_ARRAY_CLASP(TYPE_UINT8)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int16Array), TYPED_ARRAY_CLASP(TYPE_INT16)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint16Array), TYPED_ARRAY_CLASP(TYPE_UINT16)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Int32Array), TYPED_ARRAY_CLASP(TYPE_INT32)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint32Array), TYPED_ARRAY_CLASP(TYPE_UINT32)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Float32Array), TYPED_ARRAY_CLASP(TYPE_FLOAT32)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Float64Array), TYPED_ARRAY_CLASP(TYPE_FLOAT64)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray),
TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(DataView), &DataViewObject::class_},
{nullptr, 0, nullptr}
};
@ -1298,15 +1265,12 @@ static const JSStdName object_prototype_names[] = {
#undef EAGER_ATOM
#undef EAGER_CLASS_ATOM
#undef EAGER_ATOM_CLASP
#undef EAGER_ATOM_AND_CLASP
JS_PUBLIC_API(bool)
JS_ResolveStandardClass(JSContext *cx, HandleObject obj, HandleId id, bool *resolved)
{
JSRuntime *rt;
JSAtom *atom;
const JSStdName *stdnm;
unsigned i;
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
@ -1321,59 +1285,34 @@ JS_ResolveStandardClass(JSContext *cx, HandleObject obj, HandleId id, bool *reso
RootedString idstr(cx, JSID_TO_STRING(id));
/* Check whether we're resolving 'undefined', and define it if so. */
atom = rt->atomState.undefined;
if (idstr == atom) {
JSAtom *undefinedAtom = rt->atomState.undefined;
if (idstr == undefinedAtom) {
*resolved = true;
RootedValue undefinedValue(cx, UndefinedValue());
return JSObject::defineProperty(cx, obj, atom->asPropertyName(), undefinedValue,
return JSObject::defineProperty(cx, obj, undefinedAtom->asPropertyName(),
undefinedValue,
JS_PropertyStub, JS_StrictPropertyStub,
JSPROP_PERMANENT | JSPROP_READONLY);
}
/* Try for class constructors/prototypes named by well-known atoms. */
stdnm = nullptr;
for (i = 0; standard_class_atoms[i].init; i++) {
JS_ASSERT(standard_class_atoms[i].clasp);
atom = AtomStateOffsetToName(rt->atomState, standard_class_atoms[i].atomOffset);
if (idstr == atom) {
stdnm = &standard_class_atoms[i];
break;
}
}
stdnm = LookupStdName(rt, idstr, standard_class_names);
/* Try less frequently used top-level functions and constants. */
if (!stdnm)
stdnm = LookupStdName(rt, idstr, builtin_property_names);
/*
* Try even less frequently used names delegated from the global
* object to Object.prototype, but only if the Object class hasn't
* yet been initialized.
*/
if (!stdnm) {
/* Try less frequently used top-level functions and constants. */
for (i = 0; standard_class_names[i].init; i++) {
JS_ASSERT(standard_class_names[i].clasp);
atom = StdNameToPropertyName(cx, &standard_class_names[i]);
if (!atom)
return false;
if (idstr == atom) {
stdnm = &standard_class_names[i];
break;
}
}
RootedObject proto(cx);
if (!JSObject::getProto(cx, obj, &proto))
return false;
if (!stdnm && !proto) {
/*
* Try even less frequently used names delegated from the global
* object to Object.prototype, but only if the Object class hasn't
* yet been initialized.
*/
for (i = 0; object_prototype_names[i].init; i++) {
JS_ASSERT(object_prototype_names[i].clasp);
atom = StdNameToPropertyName(cx, &object_prototype_names[i]);
if (!atom)
return false;
if (idstr == atom) {
stdnm = &object_prototype_names[i];
break;
}
}
}
if (!proto)
stdnm = LookupStdName(rt, idstr, object_prototype_names);
}
if (stdnm) {
@ -1415,10 +1354,17 @@ JS_EnumerateStandardClasses(JSContext *cx, HandleObject obj)
return false;
}
/* Initialize any classes that have not been initialized yet. */
for (unsigned i = 0; standard_class_atoms[i].init; i++) {
const JSStdName &stdnm = standard_class_atoms[i];
if (!obj->as<GlobalObject>().isStandardClassResolved(stdnm.clasp)) {
/*
* Initialize any classes that have not been initialized yet. Note that
* resolving everything in standard_class_names has the effect of resolving
* everything in builtin_property_names, so we don't need to iterate over
* that separately. Moreover, we'll resolve the Object constructor as well,
* so we can also skip object_prototype_names.
*/
for (unsigned i = 0; standard_class_names[i].init; i++) {
const JSStdName &stdnm = standard_class_names[i];
// Watch out for dummy entries.
if (!stdnm.isDummy() && !obj->as<GlobalObject>().isStandardClassResolved(stdnm.clasp)) {
if (!stdnm.init(cx, obj))
return false;
}
@ -1463,6 +1409,23 @@ JS_IdentifyClassPrototype(JSContext *cx, JSObject *obj)
return js_IdentifyClassPrototype(obj);
}
extern JS_PUBLIC_API(JSProtoKey)
JS_IdToProtoKey(JSContext *cx, JS::HandleId id)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
if (!JSID_IS_ATOM(id))
return JSProto_Null;
RootedString idstr(cx, JSID_TO_STRING(id));
const JSStdName *stdnm = LookupStdName(cx->runtime(), idstr, standard_class_names);
if (!stdnm)
return JSProto_Null;
MOZ_ASSERT(MOZ_ARRAY_LENGTH(standard_class_names) == JSProto_LIMIT + 1);
return static_cast<JSProtoKey>(stdnm - standard_class_names);
}
JS_PUBLIC_API(JSObject *)
JS_GetObjectPrototype(JSContext *cx, JSObject *forObj)
{

View File

@ -1769,6 +1769,9 @@ JS_GetClassPrototype(JSContext *cx, JSProtoKey key, JSObject **objp);
extern JS_PUBLIC_API(JSProtoKey)
JS_IdentifyClassPrototype(JSContext *cx, JSObject *obj);
extern JS_PUBLIC_API(JSProtoKey)
JS_IdToProtoKey(JSContext *cx, JS::HandleId id);
/*
* Returns the original value of |Function.prototype| from the global object in
* which |forObj| was created.

View File

@ -54,7 +54,7 @@ const char * const js::TypeStrings[] = {
js_null_str,
};
#define DEFINE_PROTO_STRING(name,code,init) const char js_##name##_str[] = #name;
#define DEFINE_PROTO_STRING(name,code,init,clasp) const char js_##name##_str[] = #name;
JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
#undef DEFINE_PROTO_STRING
@ -149,7 +149,7 @@ js::InitCommonNames(JSContext *cx)
#define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
#undef COMMON_NAME_INFO
#define COMMON_NAME_INFO(name, code, init) { js_##name##_str, sizeof(#name) - 1 },
#define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
#undef COMMON_NAME_INFO
};

View File

@ -108,7 +108,7 @@ extern bool
AtomIsInterned(JSContext *cx, JSAtom *atom);
/* Well-known predefined C strings. */
#define DECLARE_PROTO_STR(name,code,init) extern const char js_##name##_str[];
#define DECLARE_PROTO_STR(name,code,init,clasp) extern const char js_##name##_str[];
JS_FOR_EACH_PROTOTYPE(DECLARE_PROTO_STR)
#undef DECLARE_PROTO_STR

View File

@ -560,6 +560,16 @@ js::GetObjectProto(JSContext *cx, JS::Handle<JSObject*> obj, JS::MutableHandle<J
return true;
}
JS_FRIEND_API(bool)
js::GetOriginalEval(JSContext *cx, HandleObject scope, MutableHandleObject eval)
{
assertSameCompartment(cx, scope);
if (!scope->global().getOrCreateObjectPrototype(cx))
return false;
eval.set(&scope->global().getOriginalEval().toObject());
return true;
}
JS_FRIEND_API(void)
js::SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const js::Value &value)
{

View File

@ -544,6 +544,10 @@ SetFunctionNativeReserved(JSObject *fun, size_t which, const JS::Value &val);
JS_FRIEND_API(bool)
GetObjectProto(JSContext *cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> proto);
JS_FRIEND_API(bool)
GetOriginalEval(JSContext *cx, JS::HandleObject scope,
JS::MutableHandleObject eval);
inline void *
GetObjectPrivate(JSObject *obj)
{

View File

@ -1945,7 +1945,7 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
return false;
global->markStandardClassInitializedNoProto(&StopIterationObject::class_);
global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
}
return true;

View File

@ -1489,7 +1489,7 @@ js_InitMathClass(JSContext *cx, HandleObject obj)
if (!JS_DefineConstDoubles(cx, Math, math_constants))
return nullptr;
obj->as<GlobalObject>().markStandardClassInitializedNoProto(&MathClass);
obj->as<GlobalObject>().setConstructor(JSProto_Math, ObjectValue(*Math));
return Math;
}

View File

@ -2907,13 +2907,13 @@ js_InitNullClass(JSContext *cx, HandleObject obj)
return nullptr;
}
#define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init) \
#define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init,clasp) \
extern JSObject *init(JSContext *cx, Handle<JSObject*> obj);
JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT)
#undef DECLARE_PROTOTYPE_CLASS_INIT
static const ClassInitializerOp lazy_prototype_init[JSProto_LIMIT] = {
#define LAZY_PROTOTYPE_INIT(name,code,init) init,
#define LAZY_PROTOTYPE_INIT(name,code,init,clasp) init,
JS_FOR_EACH_PROTOTYPE(LAZY_PROTOTYPE_INIT)
#undef LAZY_PROTOTYPE_INIT
};
@ -5391,7 +5391,7 @@ js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
if (!shape) {
const char *slotname = nullptr;
if (obj->is<GlobalObject>()) {
#define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init) \
#define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp) \
if ((code) == slot) { slotname = js_##name##_str; goto found; }
JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
#undef TEST_SLOT_MATCHES_PROTOTYPE

View File

@ -885,7 +885,7 @@ js_InitJSONClass(JSContext *cx, HandleObject obj)
if (!JS_DefineFunctions(cx, JSON, json_static_methods))
return nullptr;
global->markStandardClassInitializedNoProto(&JSONClass);
global->setConstructor(JSProto_JSON, ObjectValue(*JSON));
return JSON;
}

View File

@ -4,57 +4,98 @@
* 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/. */
/* A higher-order macro for enumerating all JSProtoKey values. */
#ifndef jsprototypes_h
#define jsprototypes_h
/* A higher-order macro for enumerating all JSProtoKey values. */
/*
* Enumerator codes in the second column must not change -- they are part of
* the JS XDR API. Also note the symbols in the third column are extern "C";
* clients should use extern "C" {} as appropriate when using this macro.
* Consumers define macros as follows:
* macro(name, code, init, clasp)
* name: The canonical name of the class.
* code: The enumerator code. There are part of the XDR API, and must not change.
* init: Initialization function. These are |extern "C";|, and clients should use
* |extern "C" {}| as appropriate when using this macro.
* clasp: The JSClass for this object, or "dummy" if it doesn't exist.
*
*
* Consumers wishing to iterate over all the JSProtoKey values, can use
* JS_FOR_EACH_PROTOTYPE. However, there are certain values that don't correspond
* to real constructors, like Null or constructors that are disabled via
* preprocessor directives. We still need to include these in the JSProtoKey list
* in order to maintain binary XDR compatibility, but we need to provide a tool
* to handle them differently. JS_FOR_PROTOTYPES fills this niche.
*
* Consumers pass two macros to JS_FOR_PROTOTYPES - |real| and |imaginary|. The
* former is invoked for entries that have real client-exposed constructors, and
* the latter is called for the rest. Consumers that don't care about this
* distinction can simply pass the same macro to both, which is exactly what
* JS_FOR_EACH_PROTOTYPE does.
*/
#define JS_FOR_EACH_PROTOTYPE(macro) \
macro(Null, 0, js_InitNullClass) \
macro(Object, 1, js_InitObjectClass) \
macro(Function, 2, js_InitFunctionClass) \
macro(Array, 3, js_InitArrayClass) \
macro(Boolean, 4, js_InitBooleanClass) \
macro(JSON, 5, js_InitJSONClass) \
macro(Date, 6, js_InitDateClass) \
macro(Math, 7, js_InitMathClass) \
macro(Number, 8, js_InitNumberClass) \
macro(String, 9, js_InitStringClass) \
macro(RegExp, 10, js_InitRegExpClass) \
macro(Error, 11, js_InitExceptionClasses) \
macro(InternalError, 12, js_InitExceptionClasses) \
macro(EvalError, 13, js_InitExceptionClasses) \
macro(RangeError, 14, js_InitExceptionClasses) \
macro(ReferenceError, 15, js_InitExceptionClasses) \
macro(SyntaxError, 16, js_InitExceptionClasses) \
macro(TypeError, 17, js_InitExceptionClasses) \
macro(URIError, 18, js_InitExceptionClasses) \
macro(Iterator, 19, js_InitIteratorClasses) \
macro(StopIteration, 20, js_InitIteratorClasses) \
macro(ArrayBuffer, 21, js_InitTypedArrayClasses) \
macro(Int8Array, 22, js_InitTypedArrayClasses) \
macro(Uint8Array, 23, js_InitTypedArrayClasses) \
macro(Int16Array, 24, js_InitTypedArrayClasses) \
macro(Uint16Array, 25, js_InitTypedArrayClasses) \
macro(Int32Array, 26, js_InitTypedArrayClasses) \
macro(Uint32Array, 27, js_InitTypedArrayClasses) \
macro(Float32Array, 28, js_InitTypedArrayClasses) \
macro(Float64Array, 29, js_InitTypedArrayClasses) \
macro(Uint8ClampedArray, 30, js_InitTypedArrayClasses) \
macro(Proxy, 31, js_InitProxyClass) \
macro(WeakMap, 32, js_InitWeakMapClass) \
macro(Map, 33, js_InitMapClass) \
macro(Set, 34, js_InitSetClass) \
macro(DataView, 35, js_InitTypedArrayClasses) \
macro(ParallelArray, 36, js_InitParallelArrayClass) \
macro(Intl, 37, js_InitIntlClass) \
macro(TypedObject, 38, js_InitTypedObjectClass) \
macro(GeneratorFunction, 39, js_InitIteratorClasses) \
#define CLASP(name) (&name##Class)
#define OCLASP(name) (&name##Object::class_)
#define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[ScalarTypeRepresentation::type])
#ifdef ENABLE_PARALLEL_JS
#define IF_PJS(real,imaginary) real
#else
#define IF_PJS(real,imaginary) imaginary
#endif
#ifdef EXPOSE_INTL_API
#define IF_INTL(real,imaginary) real
#else
#define IF_INTL(real,imaginary) imaginary
#endif
#ifdef ENABLE_BINARYDATA
#define IF_BDATA(real,imaginary) real
#else
#define IF_BDATA(real,imaginary) imaginary
#endif
#define JS_FOR_PROTOTYPES(real,imaginary) \
imaginary(Null, 0, js_InitNullClass, dummy) \
real(Object, 1, js_InitObjectClass, &JSObject::class_) \
real(Function, 2, js_InitFunctionClass, &JSFunction::class_) \
real(Array, 3, js_InitArrayClass, OCLASP(Array)) \
real(Boolean, 4, js_InitBooleanClass, OCLASP(Boolean)) \
real(JSON, 5, js_InitJSONClass, CLASP(JSON)) \
real(Date, 6, js_InitDateClass, OCLASP(Date)) \
real(Math, 7, js_InitMathClass, CLASP(Math)) \
real(Number, 8, js_InitNumberClass, OCLASP(Number)) \
real(String, 9, js_InitStringClass, OCLASP(String)) \
real(RegExp, 10, js_InitRegExpClass, OCLASP(RegExp)) \
real(Error, 11, js_InitExceptionClasses, OCLASP(Error)) \
real(InternalError, 12, js_InitExceptionClasses, OCLASP(Error)) \
real(EvalError, 13, js_InitExceptionClasses, OCLASP(Error)) \
real(RangeError, 14, js_InitExceptionClasses, OCLASP(Error)) \
real(ReferenceError, 15, js_InitExceptionClasses, OCLASP(Error)) \
real(SyntaxError, 16, js_InitExceptionClasses, OCLASP(Error)) \
real(TypeError, 17, js_InitExceptionClasses, OCLASP(Error)) \
real(URIError, 18, js_InitExceptionClasses, OCLASP(Error)) \
real(Iterator, 19, js_InitIteratorClasses, OCLASP(PropertyIterator)) \
real(StopIteration, 20, js_InitIteratorClasses, OCLASP(StopIteration)) \
real(ArrayBuffer, 21, js_InitTypedArrayClasses, &js::ArrayBufferObject::protoClass) \
real(Int8Array, 22, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_INT8)) \
real(Uint8Array, 23, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT8)) \
real(Int16Array, 24, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_INT16)) \
real(Uint16Array, 25, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT16)) \
real(Int32Array, 26, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_INT32)) \
real(Uint32Array, 27, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT32)) \
real(Float32Array, 28, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_FLOAT32)) \
real(Float64Array, 29, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_FLOAT64)) \
real(Uint8ClampedArray, 30, js_InitTypedArrayClasses, TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)) \
real(Proxy, 31, js_InitProxyClass, &ProxyObject::uncallableClass_) \
real(WeakMap, 32, js_InitWeakMapClass, OCLASP(WeakMap)) \
real(Map, 33, js_InitMapClass, OCLASP(Map)) \
real(Set, 34, js_InitSetClass, OCLASP(Set)) \
real(DataView, 35, js_InitTypedArrayClasses, OCLASP(DataView)) \
IF_PJS(real,imaginary) (ParallelArray, 36, js_InitParallelArrayClass, OCLASP(ParallelArray)) \
IF_INTL(real,imaginary) (Intl, 37, js_InitIntlClass, CLASP(Intl)) \
IF_BDATA(real,imaginary)(TypedObject, 38, js_InitTypedObjectClass, CLASP(TypedObject)) \
imaginary(GeneratorFunction, 39, js_InitIteratorClasses, dummy) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
#endif /* jsprototypes_h */

View File

@ -3367,6 +3367,6 @@ js_InitProxyClass(JSContext *cx, HandleObject obj)
return nullptr;
}
global->markStandardClassInitializedNoProto(&ProxyObject::uncallableClass_);
global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
return ctor;
}

View File

@ -73,7 +73,7 @@ typedef enum JSType {
/* Dense index into cached prototypes and class atoms for standard objects. */
typedef enum JSProtoKey {
#define PROTOKEY_AND_INITIALIZER(name,code,init) JSProto_##name = code,
#define PROTOKEY_AND_INITIALIZER(name,code,init,clasp) JSProto_##name = code,
JS_FOR_EACH_PROTOTYPE(PROTOKEY_AND_INITIALIZER)
#undef PROTOKEY_AND_INITIALIZER
JSProto_LIMIT

View File

@ -200,29 +200,22 @@ class GlobalObject : public JSObject
/*
* Lazy standard classes need a way to indicate they have been initialized.
* Otherwise, when we delete them, we might accidentally recreate them via
* a lazy initialization. We use the presence of a ctor or proto in the
* global object's slot to indicate that they've been constructed, but this
* only works for classes which have a proto and ctor. Classes which don't
* have one can call markStandardClassInitializedNoProto(), and we can
* always check whether a class is initialized by calling
* isStandardClassResolved().
* a lazy initialization. We use the presence of an object in the
* getConstructor(key) reserved slot to indicate that they've been
* initialized.
*
* Note: A few builtin objects, like JSON and Math, are not constructors,
* so getConstructor is a bit of a misnomer.
*/
bool isStandardClassResolved(const js::Class *clasp) const {
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
// If the constructor is undefined, then it hasn't been initialized.
MOZ_ASSERT(getConstructor(key).isUndefined() ||
getConstructor(key).isObject());
return !getConstructor(key).isUndefined();
}
void markStandardClassInitializedNoProto(const js::Class *clasp) {
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
// We use true so that it's obvious what we're doing (instead of, say,
// null, which might be miscontrued as an error in setting Undefined).
if (getConstructor(key).isUndefined())
setConstructor(key, BooleanValue(true));
}
private:
void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
JS_ASSERT(getConstructor(key).isUndefined());

View File

@ -446,7 +446,7 @@ struct JSAtomState
#define PROPERTYNAME_FIELD(idpart, id, text) js::FixedHeapPtr<js::PropertyName> id;
FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD)
#undef PROPERTYNAME_FIELD
#define PROPERTYNAME_FIELD(name, code, init) js::FixedHeapPtr<js::PropertyName> name;
#define PROPERTYNAME_FIELD(name, code, init, clasp) js::FixedHeapPtr<js::PropertyName> name;
JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
#undef PROPERTYNAME_FIELD
};

View File

@ -269,7 +269,7 @@ SPSEntryMarker::~SPSEntryMarker()
}
JS_FRIEND_API(jsbytecode*)
ProfileEntry::pc() volatile
ProfileEntry::pc() const volatile
{
JS_ASSERT_IF(idx != NullPCIndex, idx >= 0 && uint32_t(idx) < script()->length);
return idx == NullPCIndex ? nullptr : script()->code + idx;

View File

@ -75,6 +75,7 @@ const char* const XPCJSRuntime::mStrings[] = {
"__proto__", // IDX_PROTO
"__iterator__", // IDX_ITERATOR
"__exposedProps__", // IDX_EXPOSEDPROPS
"eval", // IDX_EVAL
};
/***************************************************************************/

View File

@ -652,6 +652,7 @@ public:
IDX_PROTO ,
IDX_ITERATOR ,
IDX_EXPOSEDPROPS ,
IDX_EVAL ,
IDX_TOTAL_COUNT // just a count of the above
};

View File

@ -50,6 +50,7 @@ support-files =
[test_bug860494.xul]
[test_bug866823.xul]
[test_bug895340.xul]
[test_xrayToJS.xul]
[test_chrometoSource.xul]
[test_cows.xul]
[test_documentdomain.xul]

View File

@ -0,0 +1,117 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=933681
-->
<window title="Mozilla Bug 933681"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=933681"
target="_blank">Mozilla Bug 933681</a>
</body>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
/** Test for ES constructors on Xrayed globals. **/
SimpleTest.waitForExplicitFinish();
const Cu = Components.utils;
let global = Cu.getGlobalForObject.bind(Cu);
simpleConstructors = ['Object', 'Function', 'Array', 'Boolean', 'Date', 'Number',
'String', 'RegExp', 'Error', 'InternalError', 'EvalError',
'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError',
'URIError', 'ArrayBuffer', 'Int8Array', 'Uint8Array',
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array', 'Uint8ClampedArray',
'WeakMap', 'Map', 'Set'];
function go() {
var iwin = document.getElementById('ifr').contentWindow;
// Test constructors that can be instantiated with zero arguments.
for (var c of simpleConstructors) {
ok(iwin[c], "Constructors appear: " + c);
is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
"we end up with the appropriate constructor: " + c);
is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c]).constructor), iwin[c],
"constructor property is set up right: " + c);
is(Object.getPrototypeOf(new iwin[c]),
Cu.unwaiveXrays(Cu.waiveXrays(iwin[c]).prototype),
"prototype is correct: " + c);
is(global(new iwin[c]), iwin, "Got the right global: " + c);
}
// Test Object in more detail.
var num = new iwin.Object(4);
is(num.valueOf(), 4, "primitive object construction works");
is(global(num), iwin, "correct global for num");
var obj = new iwin.Object();
obj.foo = 2;
var withProto = iwin.Object.create(obj);
is(global(withProto), iwin, "correct global for withProto");
is(withProto.foo, 2, "Inherits properly");
// Test Function.
var primitiveFun = new iwin.Function('return 2');
is(global(primitiveFun), iwin, "function construction works");
is(primitiveFun(), 2, "basic function works");
var doSetFoo = new iwin.Function('arg', 'arg.foo = 2;');
is(global(doSetFoo), iwin, "function with args works");
try {
doSetFoo(new Object());
ok(false, "should have thrown while setting property on object");
} catch (e) {
ok(!!/denied/.test(e), "Threw correctly: " + e);
}
var factoryFun = new iwin.Function('return {foo: 32}');
is(global(factoryFun), iwin, "proper global for factoryFun");
is(factoryFun().foo, 32, "factoryFun invokable");
is(global(factoryFun()), iwin, "minted objects live in the content scope");
// Test interface objects that don't actually construct things.
is(iwin.Math.tan(4.5), Math.tan(4.5), "Math.tan works");
is(iwin.Math.E, Math.E, "Math.E works");
var json = JSON.stringify({a: 2, b: 'hi', c: {d: 'there'}});
is(global(iwin.JSON.parse(json)), iwin, "JSON rehydrated into the right context");
is(iwin.JSON.stringify(iwin.JSON.parse(json)), json, "JSON composition identity holds");
// Test proxies.
var targetObject = new iwin.Object();
targetObject.foo = 9;
var forwardingProxy = new iwin.Proxy(targetObject, new iwin.Object());
is(global(forwardingProxy), iwin, "proxy global correct");
is(forwardingProxy.foo, 9, "forwards correctly");
// NB: COW-implemented proxy handlers are super dangerous, and we should not
// encourage them.
var handler = {get: function(target, name) { return name * 2; }, __exposedProps__: {get: 'r'}};
var doublingProxy = new iwin.Proxy(targetObject, handler);
is(global(doublingProxy), iwin, "doubling proxy global correct");
is(doublingProxy[3], 6, "Doubles correctly");
is(doublingProxy[20], 40, "Doubles correctly");
// Test eval.
var toEval = "({a: 2, b: {foo: 'bar'}, f: function() { return window; }})";
is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
is(iwin.eval(toEval).f(), iwin, "evaled function works right");
// We could also test DataView and Iterator here for completeness, but it's
// more trouble than it's worth.
SimpleTest.finish();
}
]]>
</script>
<iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
</window>

View File

@ -807,12 +807,36 @@ XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper,
// Check for expando properties first. Note that the expando object lives
// in the target compartment.
bool found = false;
if (expando) {
JSAutoCompartment ac(cx, expando);
if (!JS_GetPropertyDescriptorById(cx, expando, id, 0, desc))
return false;
found = !!desc.object();
}
if (desc.object()) {
// Next, check for ES builtins.
if (!found && JS_IsGlobalObject(target)) {
JSProtoKey key = JS_IdToProtoKey(cx, id);
JSAutoCompartment ac(cx, target);
if (key != JSProto_Null) {
MOZ_ASSERT(key < JSProto_LIMIT);
RootedObject constructor(cx);
if (!JS_GetClassObject(cx, target, key, constructor.address()))
return false;
MOZ_ASSERT(constructor);
desc.value().set(ObjectValue(*constructor));
found = true;
} else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EVAL)) {
RootedObject eval(cx);
if (!js::GetOriginalEval(cx, target, &eval))
return false;
desc.value().set(ObjectValue(*eval));
found = true;
}
}
if (found) {
if (!JS_WrapPropertyDescriptor(cx, desc))
return false;
// Pretend the property lives on the wrapper.

View File

@ -18,6 +18,7 @@
#include "nsContentCreatorFunctions.h"
#include "nsContentList.h"
#include "nsStyleSet.h"
#include "nsIDOMMutationEvent.h"
using namespace mozilla;
using namespace mozilla::dom;

View File

@ -278,15 +278,12 @@ cprMoveMsgToQueue(cpr_msg_queue_t *msgq);
cprMsgQueue_t
cprCreateMessageQueue (const char *name, uint16_t depth)
{
static const char fname[] = "cprCreateMessageQueue";
cpr_msg_queue_t *msgq;
key_t key;
static int key_id = 100; /* arbitrary starting number */
struct msqid_ds buf;
msgq =(cpr_msg_queue_t *)cpr_calloc(1, sizeof(cpr_msg_queue_t));
if (msgq == NULL) {
CPR_ERROR("%s: Malloc failed: %s\n", fname,
CPR_ERROR("%s: Malloc failed: %s\n", __FUNCTION__,
name ? name : unnamed_string);
errno = ENOMEM;
return NULL;
@ -294,45 +291,13 @@ cprCreateMessageQueue (const char *name, uint16_t depth)
msgq->name = name ? name : unnamed_string;
/*
* Find a unique key
*/
key = ftok("/proc/self", key_id++);
CSFLogDebug(logTag, "key = %x\n", key);
if (key == -1) {
CPR_ERROR("%s: Key generation failed: %d\n", fname, errno);
cpr_free(msgq);
return NULL;
}
/*
* Set creation flag so that OS will create the message queue
*/
msgq->queueId = msgget(key, (IPC_EXCL | IPC_CREAT | 0666));
if (msgq->queueId == -1) {
if (errno == EEXIST) {
CSFLogDebug(logTag, "Q exists so first remove it and then create again\n");
/* Remove message queue */
msgq->queueId = msgget(key, (IPC_CREAT | 0666));
if (msgctl(msgq->queueId, IPC_RMID, &buf) == -1) {
CPR_ERROR("%s: Destruction failed: %s: %d\n", fname,
msgq->name, errno);
return NULL;
}
msgq->queueId = msgget(key, (IPC_CREAT | 0666));
}
} else {
CSFLogDebug(logTag, "there was no preexisting q..\n");
}
msgq->queueId = msgget(IPC_PRIVATE, (IPC_EXCL | IPC_CREAT | 0666));
if (msgq->queueId == -1) {
CPR_ERROR("%s: Creation failed: %s: %d\n", fname, name, errno);
CPR_ERROR("%s: Creation failed: %s: %d\n", __FUNCTION__, name, errno);
if (errno == EEXIST) {
}
@ -349,7 +314,7 @@ cprCreateMessageQueue (const char *name, uint16_t depth)
*/
if (pthread_mutex_init(&msgq->mutex, NULL) != 0) {
CPR_ERROR("%s: Failed to create msg queue (%s) mutex: %d\n",
fname, name, errno);
__FUNCTION__, name, errno);
(void) msgctl(msgq->queueId, IPC_RMID, &buf);
cpr_free(msgq);
return NULL;
@ -359,14 +324,14 @@ cprCreateMessageQueue (const char *name, uint16_t depth)
* Set the extended message queue depth (within bounds)
*/
if (depth > CPR_MAX_MSG_Q_DEPTH) {
CPR_INFO("%s: Depth too large (%d) reset to %d\n", fname, depth,
CPR_INFO("%s: Depth too large (%d) reset to %d\n", __FUNCTION__, depth,
CPR_MAX_MSG_Q_DEPTH);
depth = CPR_MAX_MSG_Q_DEPTH;
}
if (depth < OS_MSGTQL) {
if (depth) {
CPR_INFO("%s: Depth too small (%d) reset to %d\n", fname, depth, OS_MSGTQL);
CPR_INFO("%s: Depth too small (%d) reset to %d\n", __FUNCTION__, depth, OS_MSGTQL);
}
depth = OS_MSGTQL;
}

View File

@ -71,7 +71,7 @@ DeviceInfoDS* DeviceInfoDS::Create(const int32_t id)
}
DeviceInfoDS::DeviceInfoDS(const int32_t id)
: DeviceInfoImpl(id), _dsDevEnum(NULL), _dsMonikerDevEnum(NULL),
: DeviceInfoImpl(id), _dsDevEnum(NULL),
_CoUninitializeIsRequired(true)
{
// 1) Initialize the COM library (make Windows load the DLLs).
@ -116,7 +116,6 @@ DeviceInfoDS::DeviceInfoDS(const int32_t id)
DeviceInfoDS::~DeviceInfoDS()
{
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
RELEASE_AND_CLEAR(_dsDevEnum);
if (_CoUninitializeIsRequired)
{
@ -173,7 +172,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
{
// enumerate all video capture devices
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
IEnumMoniker* _dsMonikerDevEnum = NULL;
HRESULT hr =
_dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&_dsMonikerDevEnum, 0);
@ -182,6 +181,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"Failed to enumerate CLSID_SystemDeviceEnum, error 0x%x."
" No webcam exist?", hr);
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
return 0;
}
@ -227,6 +227,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
webrtc::kTraceVideoCapture, _id,
"Failed to convert device name to UTF8. %d",
GetLastError());
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
return -1;
}
}
@ -258,6 +259,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
webrtc::kTraceVideoCapture, _id,
"Failed to convert device name to UTF8. %d",
GetLastError());
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
return -1;
}
if (productUniqueIdUTF8
@ -285,6 +287,7 @@ int32_t DeviceInfoDS::GetDeviceInfo(
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, "%s %s",
__FUNCTION__, deviceNameUTF8);
}
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
return index;
}
@ -303,8 +306,8 @@ IBaseFilter * DeviceInfoDS::GetDeviceFilter(
return NULL;
}
IEnumMoniker* _dsMonikerDevEnum = NULL;
// enumerate all video capture devices
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&_dsMonikerDevEnum, 0);
if (hr != NOERROR)
@ -312,6 +315,7 @@ IBaseFilter * DeviceInfoDS::GetDeviceFilter(
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
"Failed to enumerate CLSID_SystemDeviceEnum, error 0x%x."
" No webcam exist?", hr);
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
return 0;
}
_dsMonikerDevEnum->Reset();
@ -379,6 +383,7 @@ IBaseFilter * DeviceInfoDS::GetDeviceFilter(
pM->Release();
}
}
RELEASE_AND_CLEAR(_dsMonikerDevEnum);
return captureFilter;
}

View File

@ -97,7 +97,6 @@ protected:
private:
ICreateDevEnum* _dsDevEnum;
IEnumMoniker* _dsMonikerDevEnum;
bool _CoUninitializeIsRequired;
};

View File

@ -635,27 +635,35 @@ nsHostResolver::ResolveHost(const char *host,
unspecHe->rec->HasUsableResult(flags) &&
TimeStamp::NowLoRes() <= (he->rec->expiration +
TimeDuration::FromSeconds(mGracePeriod * 60))) {
MOZ_ASSERT(unspecHe->rec->addr_info || unspecHe->rec->negative,
"Entry should be resolved or negative.");
LOG((" Trying AF_UNSPEC entry for [%s] af: %s.\n",
host, (af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
// Search for any valid address in the AF_UNSPEC entry
// in the cache (not blacklisted and from the right
// family).
NetAddrElement *addrIter =
unspecHe->rec->addr_info->mAddresses.getFirst();
he->rec->addr_info = nullptr;
while (addrIter) {
if ((af == addrIter->mAddress.inet.family) &&
!unspecHe->rec->Blacklisted(&addrIter->mAddress)) {
if (!he->rec->addr_info) {
he->rec->addr_info = new AddrInfo(
unspecHe->rec->addr_info->mHostName,
unspecHe->rec->addr_info->mCanonicalName);
if (unspecHe->rec->negative) {
he->rec->negative = unspecHe->rec->negative;
} else if (he->rec->addr_info) {
// Search for any valid address in the AF_UNSPEC entry
// in the cache (not blacklisted and from the right
// family).
NetAddrElement *addrIter =
unspecHe->rec->addr_info->mAddresses.getFirst();
while (addrIter) {
if ((af == addrIter->mAddress.inet.family) &&
!unspecHe->rec->Blacklisted(&addrIter->mAddress)) {
if (!he->rec->addr_info) {
he->rec->addr_info = new AddrInfo(
unspecHe->rec->addr_info->mHostName,
unspecHe->rec->addr_info->mCanonicalName);
}
he->rec->addr_info->AddAddress(
new NetAddrElement(*addrIter));
}
he->rec->addr_info->AddAddress(
new NetAddrElement(*addrIter));
addrIter = addrIter->getNext();
}
addrIter = addrIter->getNext();
}
if (he->rec->HasUsableResult(flags)) {
result = he->rec;
@ -976,7 +984,7 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *r
rec->usingAnyThread = false;
}
if (rec->addr_info && !mShutdown) {
if (!mShutdown) {
// add to mEvictionQ
PR_APPEND_LINK(rec, &mEvictionQ);
NS_ADDREF(rec);
@ -1170,7 +1178,8 @@ CacheEntryEnumerator(PLDHashTable *table, PLDHashEntryHdr *entry,
// We don't pay attention to address literals, only resolved domains.
// Also require a host.
nsHostRecord *rec = static_cast<nsHostDBEnt*>(entry)->rec;
if (!rec->addr_info || !rec->host) {
MOZ_ASSERT(rec, "rec should never be null here!");
if (!rec || !rec->addr_info || !rec->host) {
return PL_DHASH_NEXT;
}

View File

@ -27,7 +27,8 @@ RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
CERTUTIL="$OBJDIR/dist/bin/certutil"
NOISE_FILE=`mktemp`
dd if=/dev/urandom of="$NOISE_FILE" bs=1024 count=1
# Make a good effort at putting something unique in the noise file.
date +%s%N > "$NOISE_FILE"
PASSWORD_FILE=`mktemp`
function cleanup {
@ -35,8 +36,8 @@ function cleanup {
}
if [ ! -f "$RUN_MOZILLA" ]; then
echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\'"
exit $E_BADARGS
echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\' - I'll try without it"
RUN_MOZILLA=""
fi
if [ ! -f "$CERTUTIL" ]; then

View File

@ -49,7 +49,9 @@ NS_IMPL_CI_INTERFACE_GETTER4(
class AsyncStatementClassInfo : public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS
MOZ_CONSTEXPR AsyncStatementClassInfo() {}
NS_DECL_ISUPPORTS_INHERITED
NS_IMETHODIMP
GetInterfaces(uint32_t *_count, nsIID ***_array)

View File

@ -51,7 +51,9 @@ NS_IMPL_CI_INTERFACE_GETTER5(
class StatementClassInfo : public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS
MOZ_CONSTEXPR StatementClassInfo() {}
NS_DECL_ISUPPORTS_INHERITED
NS_IMETHODIMP
GetInterfaces(uint32_t *_count, nsIID ***_array)

View File

@ -1,3 +1,4 @@
/* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
// Test timeout (seconds)
var gTimeoutSeconds = 30;
var gConfig;
@ -364,7 +365,12 @@ Tester.prototype = {
.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)
{
this.MemoryStats.dump((l) => { this.dumper.dump(l + "\n"); });
this.MemoryStats.dump((l) => { this.dumper.dump(l + "\n"); },
this.currentTestIndex,
this.currentTest.path,
gConfig.dumpOutputDirectory,
gConfig.dumpAboutMemoryAfterTest,
gConfig.dumpDMDAfterTest);
}
// Note the test run time

View File

@ -190,7 +190,8 @@ class MochitestRunner(MozbuildObject):
no_autorun=False, repeat=0, run_until_failure=False, slow=False,
chunk_by_dir=0, total_chunks=None, this_chunk=None, jsdebugger=False,
debug_on_failure=False, start_at=None, end_at=None, e10s=False,
dmd=False):
dmd=False, dump_output_directory=None, dump_about_memory_after_test=False,
dump_dmd_after_test=False):
"""Runs a mochitest.
test_file is a path to a test file. It can be a relative path from the
@ -298,6 +299,9 @@ class MochitestRunner(MozbuildObject):
options.startAt = start_at
options.endAt = end_at
options.e10s = e10s
options.dumpAboutMemoryAfterTest = dump_about_memory_after_test
options.dumpDMDAfterTest = dump_dmd_after_test
options.dumpOutputDirectory = dump_output_directory
mozinfo.update({"e10s": e10s}) # for test manifest parsing.
options.failureFile = failure_file_path
@ -443,6 +447,18 @@ def MochitestCommand(func):
help='Run tests with DMD active.')
func = dmd(func)
dumpAboutMemory = CommandArgument('--dump-about-memory-after-test', action='store_true',
help='Dump an about:memory log after every test.')
func = dumpAboutMemory(func)
dumpDMD = CommandArgument('--dump-dmd-after-test', action='store_true',
help='Dump a DMD log after every test.')
func = dumpDMD(func)
dumpOutputDirectory = CommandArgument('--dump-output-directory', action='store',
help='Specifies the directory in which to place dumped memory reports.')
func = dumpOutputDirectory(func)
path = CommandArgument('test_file', default=None, nargs='?',
metavar='TEST',
help='Test to run. Can be specified as a single file, a ' \

View File

@ -357,6 +357,26 @@ class MochitestOptions(optparse.OptionParser):
"dest": "dmdPath",
"help": "Specifies the path to the directory containing the shared library for DMD.",
}],
[["--dump-output-directory"],
{ "action": "store",
"default": None,
"dest": "dumpOutputDirectory",
"help": "Specifies the directory in which to place dumped memory reports.",
}],
[["--dump-about-memory-after-test"],
{ "action": "store_true",
"default": False,
"dest": "dumpAboutMemoryAfterTest",
"help": "Produce an about:memory dump after each test in the directory specified "
"by --dump-output-directory."
}],
[["--dump-dmd-after-test"],
{ "action": "store_true",
"default": False,
"dest": "dumpDMDAfterTest",
"help": "Produce a DMD dump after each test in the directory specified "
"by --dump-output-directory."
}],
]
def __init__(self, **kwargs):
@ -505,6 +525,14 @@ class MochitestOptions(optparse.OptionParser):
if not options.repeat:
options.repeat = 29
if options.dumpOutputDirectory is None:
options.dumpOutputDirectory = tempfile.gettempdir()
if options.dumpAboutMemoryAfterTest or options.dumpDMDAfterTest:
if not os.path.isdir(options.dumpOutputDirectory):
self.error('--dump-output-directory not a directory: %s' %
options.dumpOutputDirectory)
return options

View File

@ -344,6 +344,12 @@ class MochitestUtilsMixin(object):
self.urlOpts.append("runSlower=true")
if options.debugOnFailure:
self.urlOpts.append("debugOnFailure=true")
if options.dumpOutputDirectory:
self.urlOpts.append("dumpOutputDirectory=%s" % encodeURIComponent(options.dumpOutputDirectory))
if options.dumpAboutMemoryAfterTest:
self.urlOpts.append("dumpAboutMemoryAfterTest=true")
if options.dumpDMDAfterTest:
self.urlOpts.append("dumpDMDAfterTest=true")
def buildTestPath(self, options):
""" Build the url path to the specific test harness and test file or directory

View File

@ -212,6 +212,9 @@ class RemoteOptions(MochitestOptions):
tempPort = options.httpPort
tempSSL = options.sslPort
tempIP = options.webServer
# We are going to override this option later anyway, just pretend
# like it's not set for verification purposes.
options.dumpOutputDirectory = None
options = MochitestOptions.verifyOptions(self, options, mochitest)
options.webServer = tempIP
options.app = temp
@ -588,6 +591,8 @@ def main():
dm.pushFile(os.path.join(options.dmdPath, dmdLibrary), dmdPathOnDevice)
options.dmdPath = deviceRoot
options.dumpOutputDirectory = deviceRoot
procName = options.app.split('/')[-1]
if (dm.processExist(procName)):
dm.killProcess(procName)

View File

@ -1,3 +1,9 @@
/* -*- js-indent-level: 4; tab-width: 4; indent-tabs-mode: nil -*- */
/* vim:set ts=4 sw=4 sts=4 et: */
/* 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/. */
var MemoryStats = {};
/**
@ -18,15 +24,25 @@ MemoryStats._hasMemoryStatistics.vsizeMaxContiguous = MEM_STAT_UNKNOWN;
MemoryStats._hasMemoryStatistics.residentFast = MEM_STAT_UNKNOWN;
MemoryStats._hasMemoryStatistics.heapAllocated = MEM_STAT_UNKNOWN;
MemoryStats.dump = function (dumpFn) {
var mrm;
MemoryStats._getService = function (className, interfaceName) {
var service;
try {
mrm = Cc["@mozilla.org/memory-reporter-manager;1"]
.getService(Ci.nsIMemoryReporterManager);
service = Cc[className].getService(Ci[interfaceName]);
} catch (e) {
mrm = SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"]
.getService(SpecialPowers.Ci.nsIMemoryReporterManager);
service = SpecialPowers.Cc[className]
.getService(SpecialPowers.Ci[interfaceName]);
}
return service;
}
MemoryStats.dump = function (dumpFn,
testNumber,
testURL,
dumpOutputDirectory,
dumpAboutMemory,
dumpDMD) {
var mrm = MemoryStats._getService("@mozilla.org/memory-reporter-manager;1",
"nsIMemoryReporterManager");
for (var stat in MemoryStats._hasMemoryStatistics) {
var supported = MemoryStats._hasMemoryStatistics[stat];
var firstAccess = false;
@ -46,4 +62,21 @@ MemoryStats.dump = function (dumpFn) {
dumpFn("TEST-INFO | MEMORY STAT " + stat + " not supported in this build configuration.");
}
}
if (dumpAboutMemory) {
var dumpfile = dumpOutputDirectory + "/about-memory-" + testNumber + ".json.gz";
dumpFn("TEST-INFO | " + testURL + " | MEMDUMP-START " + dumpfile);
var md = MemoryStats._getService("@mozilla.org/memory-info-dumper;1",
"nsIMemoryInfoDumper");
md.dumpMemoryReportsToNamedFile(dumpfile, function () {
dumpFn("TEST-INFO | " + testURL + " | MEMDUMP-END");
}, null);
}
if (dumpDMD && typeof(DMDReportAndDump) != undefined) {
var dumpfile = dumpOutputDirectory + "/dmd-" + testNumber + ".txt";
dumpFn("TEST-INFO | " + testURL + " | DMD-DUMP " + dumpfile);
DMDReportAndDump(dumpfile);
}
};

View File

@ -1,3 +1,4 @@
/* -*- js-indent-level: 4 -*- */
/*
* e10s event dispatcher from content->chrome
*
@ -84,6 +85,9 @@ TestRunner._expectedMaxAsserts = 0;
TestRunner.timeout = 5 * 60 * 1000; // 5 minutes.
TestRunner.maxTimeouts = 4; // halt testing after too many timeouts
TestRunner.runSlower = false;
TestRunner.dumpOutputDirectory = "";
TestRunner.dumpAboutMemoryAfterTest = false;
TestRunner.dumpDMDAfterTest = false;
TestRunner.slowestTestTime = 0;
TestRunner.slowestTestURL = "";
@ -409,7 +413,11 @@ TestRunner.testFinished = function(tests) {
TestRunner._lastTestFinished = TestRunner._currentTest;
TestRunner._loopIsRestarting = false;
MemoryStats.dump(TestRunner.log);
MemoryStats.dump(TestRunner.log, TestRunner._currentTest,
TestRunner.currentTestURL,
TestRunner.dumpOutputDirectory,
TestRunner.dumpAboutMemoryAfterTest,
TestRunner.dumpDMDAfterTest);
function cleanUpCrashDumpFiles() {
if (!SpecialPowers.removeExpectedCrashDumpFiles(TestRunner._expectingProcessCrash)) {

View File

@ -131,6 +131,18 @@ if (params.runSlower) {
TestRunner.runSlower = true;
}
if (params.dumpOutputDirectory) {
TestRunner.dumpOutputDirectory = params.dumpOutputDirectory;
}
if (params.dumpAboutMemoryAfterTest) {
TestRunner.dumpAboutMemoryAfterTest = true;
}
if (params.dumpDMDAfterTest) {
TestRunner.dumpDMDAfterTest = true;
}
var gTestList = [];
var RunSet = {}
RunSet.runall = function(e) {

View File

@ -13,6 +13,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Likely.h"
#include "mozilla/MathAlgorithms.h"
#include "base/histogram.h"
#include "base/pickle.h"
@ -45,6 +46,8 @@
#include "nsNetUtil.h"
#include "plstr.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/ThreadHangStats.h"
#include "mozilla/ProcessedStack.h"
#include "mozilla/Mutex.h"
#include "mozilla/FileUtils.h"
@ -247,6 +250,7 @@ public:
static void RecordChromeHang(uint32_t duration,
Telemetry::ProcessedStack &aStack);
#endif
static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id);
static int64_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
struct Stat {
@ -319,6 +323,9 @@ private:
Mutex mHashMutex;
HangReports mHangReports;
Mutex mHangReportsMutex;
// mThreadHangStats stores recorded, inactive thread hang stats
Vector<Telemetry::ThreadHangStats> mThreadHangStats;
Mutex mThreadHangStatsMutex;
nsCOMPtr<nsIMemoryReporter> mReporter;
CombinedStacks mLateWritesStacks; // This is collected out of the main thread.
@ -342,6 +349,7 @@ TelemetryImpl::SizeOfIncludingThisHelper(mozilla::MallocSizeOf aMallocSizeOf)
n += mSanitizedSQL.SizeOfExcludingThis(nullptr, aMallocSizeOf);
n += mTrackedDBs.SizeOfExcludingThis(nullptr, aMallocSizeOf);
n += mHangReports.SizeOfExcludingThis();
n += mThreadHangStats.sizeOfExcludingThis(aMallocSizeOf);
return n;
}
@ -940,6 +948,7 @@ mHistogramMap(Telemetry::HistogramCount),
mCanRecord(XRE_GetProcessType() == GeckoProcessType_Default),
mHashMutex("Telemetry::mHashMutex"),
mHangReportsMutex("Telemetry::mHangReportsMutex"),
mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex"),
mCachedTelemetryData(false),
mLastShutdownTime(0),
mFailedLockCount(0)
@ -1705,6 +1714,180 @@ ReadStack(const char *aFileName, Telemetry::ProcessedStack &aStack)
aStack = stack;
}
static JSObject*
CreateJSTimeHistogram(JSContext* cx, const Telemetry::TimeHistogram& time)
{
/* Create JS representation of TimeHistogram,
in the format of Chromium-style histograms. */
JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
if (!ret) {
return nullptr;
}
if (!JS_DefineProperty(cx, ret, "min",
UINT_TO_JSVAL(time.GetBucketMin(0)),
nullptr, nullptr, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, ret, "max",
UINT_TO_JSVAL(time.GetBucketMax(
ArrayLength(time) - 1)),
nullptr, nullptr, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, ret, "histogram_type",
INT_TO_JSVAL(nsITelemetry::HISTOGRAM_EXPONENTIAL),
nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
// TODO: calculate "sum", "log_sum", and "log_sum_squares"
if (!JS_DefineProperty(cx, ret, "sum", INT_TO_JSVAL(0),
nullptr, nullptr, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, ret, "log_sum", DOUBLE_TO_JSVAL(0.0),
nullptr, nullptr, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, ret, "log_sum_squares", DOUBLE_TO_JSVAL(0.0),
nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
JS::RootedObject ranges(
cx, JS_NewArrayObject(cx, ArrayLength(time) + 1, nullptr));
JS::RootedObject counts(
cx, JS_NewArrayObject(cx, ArrayLength(time) + 1, nullptr));
if (!ranges || !counts) {
return nullptr;
}
/* In a Chromium-style histogram, the first bucket is an "under" bucket
that represents all values below the histogram's range. */
JS::RootedValue underRange(cx, INT_TO_JSVAL(time.GetBucketMin(0)));
JS::RootedValue underCount(cx, INT_TO_JSVAL(0));
if (!JS_SetElement(cx, ranges, 0, &underRange) ||
!JS_SetElement(cx, counts, 0, &underCount)) {
return nullptr;
}
for (size_t i = 0; i < ArrayLength(time); i++) {
JS::RootedValue range(cx, UINT_TO_JSVAL(time.GetBucketMax(i)));
JS::RootedValue count(cx, UINT_TO_JSVAL(time[i]));
if (!JS_SetElement(cx, ranges, i + 1, &range) ||
!JS_SetElement(cx, counts, i + 1, &count)) {
return nullptr;
}
}
if (!JS_DefineProperty(cx, ret, "ranges", OBJECT_TO_JSVAL(ranges),
nullptr, nullptr, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, ret, "counts", OBJECT_TO_JSVAL(counts),
nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
return ret;
}
static JSObject*
CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang)
{
JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
if (!ret) {
return nullptr;
}
const Telemetry::HangHistogram::Stack& hangStack = hang.GetStack();
JS::RootedObject stack(cx,
JS_NewArrayObject(cx, hangStack.length(), nullptr));
if (!ret) {
return nullptr;
}
for (size_t i = 0; i < hangStack.length(); i++) {
JS::RootedString string(cx, JS_NewStringCopyZ(cx, hangStack[i]));
JS::RootedValue frame(cx, STRING_TO_JSVAL(string));
if (!JS_SetElement(cx, stack, i, &frame)) {
return nullptr;
}
}
JS::RootedObject time(cx, CreateJSTimeHistogram(cx, hang));
if (!time ||
!JS_DefineProperty(cx, ret, "stack", OBJECT_TO_JSVAL(stack),
nullptr, nullptr, JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, ret, "histogram", OBJECT_TO_JSVAL(time),
nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
return ret;
}
static JSObject*
CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread)
{
JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
if (!ret) {
return nullptr;
}
JS::RootedString name(cx, JS_NewStringCopyZ(cx, thread.GetName()));
if (!name ||
!JS_DefineProperty(cx, ret, "name", STRING_TO_JSVAL(name),
nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
JS::RootedObject activity(cx, CreateJSTimeHistogram(cx, thread.mActivity));
if (!activity ||
!JS_DefineProperty(cx, ret, "activity", OBJECT_TO_JSVAL(activity),
nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
JS::RootedObject hangs(cx, JS_NewArrayObject(cx, 0, nullptr));
if (!hangs) {
return nullptr;
}
for (size_t i = 0; i < thread.mHangs.length(); i++) {
JS::RootedObject obj(cx, CreateJSHangHistogram(cx, thread.mHangs[i]));
JS::RootedValue hang(cx, OBJECT_TO_JSVAL(obj));
if (!JS_SetElement(cx, hangs, i, &hang)) {
return nullptr;
}
}
if (!JS_DefineProperty(cx, ret, "hangs", OBJECT_TO_JSVAL(hangs),
nullptr, nullptr, JSPROP_ENUMERATE)) {
return nullptr;
}
return ret;
}
NS_IMETHODIMP
TelemetryImpl::GetThreadHangStats(JSContext* cx, JS::Value* ret)
{
JS::RootedObject retObj(cx, JS_NewArrayObject(cx, 0, nullptr));
if (!retObj) {
return NS_ERROR_FAILURE;
}
size_t threadIndex = 0;
/* First add active threads; we need to hold |iter| (and its lock)
throughout this method to avoid a race condition where a thread can
be recorded twice if the thread is destroyed while this method is
running */
BackgroundHangMonitor::ThreadHangStatsIterator iter;
for (Telemetry::ThreadHangStats* histogram = iter.GetNext();
histogram; histogram = iter.GetNext()) {
JS::RootedObject obj(cx,
CreateJSThreadHangStats(cx, *histogram));
JS::RootedValue thread(cx, OBJECT_TO_JSVAL(obj));
if (!JS_SetElement(cx, retObj, threadIndex++, &thread)) {
return NS_ERROR_FAILURE;
}
}
// Add saved threads next
MutexAutoLock autoLock(mThreadHangStatsMutex);
for (size_t i = 0; i < mThreadHangStats.length(); i++) {
JS::RootedObject obj(cx,
CreateJSThreadHangStats(cx, mThreadHangStats[i]));
JS::RootedValue thread(cx, OBJECT_TO_JSVAL(obj));
if (!JS_SetElement(cx, retObj, threadIndex++, &thread)) {
return NS_ERROR_FAILURE;
}
}
*ret = OBJECT_TO_JSVAL(retObj);
return NS_OK;
}
void
TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir)
{
@ -2058,6 +2241,17 @@ TelemetryImpl::RecordChromeHang(uint32_t duration,
}
#endif
void
TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats)
{
if (!sTelemetry || !sTelemetry->mCanRecord)
return;
MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex);
sTelemetry->mThreadHangStats.append(Move(aStats));
}
NS_IMPL_ISUPPORTS1(TelemetryImpl, nsITelemetry)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
@ -2223,6 +2417,11 @@ void RecordChromeHang(uint32_t duration,
}
#endif
void RecordThreadHangStats(ThreadHangStats& aStats)
{
TelemetryImpl::RecordThreadHangStats(aStats);
}
ProcessedStack::ProcessedStack()
{
}
@ -2439,6 +2638,41 @@ WriteFailedProfileLock(nsIFile* aProfileDir)
seekStream->SetEOF();
}
void
TimeHistogram::Add(PRIntervalTime aTime)
{
uint32_t timeMs = PR_IntervalToMilliseconds(aTime);
size_t index = mozilla::FloorLog2(timeMs);
operator[](index)++;
}
uint32_t
HangHistogram::GetHash(const Stack& aStack)
{
uint32_t hash = 0;
for (const char* const* label = aStack.begin();
label != aStack.end(); label++) {
/* We only need to hash the pointer instead of the text content
because we are assuming constant pointers */
hash = AddToHash(hash, *label);
}
return hash;
}
bool
HangHistogram::operator==(const HangHistogram& aOther) const
{
if (mHash != aOther.mHash) {
return false;
}
if (mStack.length() != aOther.mStack.length()) {
return false;
}
return PodEqual(mStack.begin(), aOther.mStack.begin(), mStack.length());
}
} // namespace Telemetry
} // namespace mozilla

View File

@ -175,6 +175,20 @@ void RecordChromeHang(uint32_t duration,
ProcessedStack &aStack);
#endif
class ThreadHangStats;
/**
* Move a ThreadHangStats to Telemetry storage. Normally Telemetry queries
* for active ThreadHangStats through BackgroundHangMonitor, but once a
* thread exits, the thread's copy of ThreadHangStats needs to be moved to
* inside Telemetry using this function.
*
* @param aStats ThreadHangStats to save; the data inside aStats
* will be moved and aStats should be treated as
* invalid after this function returns
*/
void RecordThreadHangStats(ThreadHangStats& aStats);
/**
* Record a failed attempt at locking the user's profile.
*

View File

@ -311,6 +311,16 @@ TelemetryPing.prototype = {
return ret;
},
getThreadHangStats: function getThreadHangStats(stats) {
stats.forEach((thread) => {
thread.activity = this.packHistogram(thread.activity);
thread.hangs.forEach((hang) => {
hang.histogram = this.packHistogram(hang.histogram);
});
});
return stats;
},
/**
* Descriptive metadata
*
@ -548,6 +558,7 @@ TelemetryPing.prototype = {
histograms: this.getHistograms(Telemetry.histogramSnapshots),
slowSQL: Telemetry.slowSQL,
chromeHangs: Telemetry.chromeHangs,
threadHangStats: this.getThreadHangStats(Telemetry.threadHangStats),
lateWrites: Telemetry.lateWrites,
addonHistograms: this.getAddonHistograms(),
addonDetails: AddonManagerPrivate.getTelemetryDetails(),

View File

@ -0,0 +1,113 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* 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 mozilla_BackgroundHangTelemetry_h
#define mozilla_BackgroundHangTelemetry_h
#include "mozilla/Array.h"
#include "mozilla/Assertions.h"
#include "mozilla/Move.h"
#include "mozilla/Mutex.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Vector.h"
#include "nsString.h"
#include "prinrval.h"
namespace mozilla {
namespace Telemetry {
static const size_t kTimeHistogramBuckets = 8 * sizeof(PRIntervalTime);
/* TimeHistogram is an efficient histogram that puts time durations into
exponential (base 2) buckets; times are accepted in PRIntervalTime and
stored in milliseconds. */
class TimeHistogram : public mozilla::Array<uint32_t, kTimeHistogramBuckets>
{
public:
TimeHistogram()
{
mozilla::PodArrayZero(*this);
}
// Get minimum (inclusive) range of bucket in milliseconds
uint32_t GetBucketMin(size_t aBucket) const {
MOZ_ASSERT(aBucket < ArrayLength(*this));
return (1u << aBucket) & ~1u; // Bucket 0 starts at 0, not 1
}
// Get maximum (inclusive) range of bucket in milliseconds
uint32_t GetBucketMax(size_t aBucket) const {
MOZ_ASSERT(aBucket < ArrayLength(*this));
return (1u << (aBucket + 1u)) - 1u;
}
void Add(PRIntervalTime aTime);
};
/* A hang histogram consists of a stack associated with the
hang, along with a time histogram of the hang times. */
class HangHistogram : public TimeHistogram
{
public:
typedef mozilla::Vector<const char*, 8> Stack;
private:
static uint32_t GetHash(const Stack& aStack);
Stack mStack;
// Use a hash to speed comparisons
const uint32_t mHash;
public:
explicit HangHistogram(Stack&& aStack)
: mStack(mozilla::Move(aStack))
, mHash(GetHash(mStack))
{
}
HangHistogram(HangHistogram&& aOther)
: TimeHistogram(mozilla::Move(aOther))
, mStack(mozilla::Move(aOther.mStack))
, mHash(mozilla::Move(aOther.mHash))
{
}
bool operator==(const HangHistogram& aOther) const;
bool operator!=(const HangHistogram& aOther) const
{
return !operator==(aOther);
}
const Stack& GetStack() const {
return mStack;
}
};
/* Thread hang stats consist of
- thread name
- time histogram of all task run times
- hang histograms of individual hangs. */
class ThreadHangStats
{
private:
nsAutoCString mName;
public:
TimeHistogram mActivity;
mozilla::Vector<HangHistogram, 4> mHangs;
explicit ThreadHangStats(const char* aName)
: mName(aName)
{
}
ThreadHangStats(ThreadHangStats&& aOther)
: mName(mozilla::Move(aOther.mName))
, mActivity(mozilla::Move(aOther.mActivity))
, mHangs(mozilla::Move(aOther.mHangs))
{
}
const char* GetName() const {
return mName.get();
}
};
} // namespace Telemetry
} // namespace mozilla
#endif // mozilla_BackgroundHangTelemetry_h

View File

@ -16,6 +16,7 @@ XPIDL_MODULE = 'telemetry'
EXPORTS.mozilla += [
'ProcessedStack.h',
'Telemetry.h',
'ThreadHangStats.h',
]
SOURCES += [

View File

@ -99,6 +99,23 @@ interface nsITelemetry : nsISupports
[implicit_jscontext]
readonly attribute jsval chromeHangs;
/*
* An array of thread hang stats,
* [<thread>, <thread>, ...]
* <thread> represents a single thread,
* {"name": "<name>",
* "activity": <time>,
* "hangs": [<hang>, <hang>, ...]}
* <time> represents a histogram of time intervals in milliseconds,
* with the same format as histogramSnapshots
* <hang> represents a particular hang,
* {"stack": <stack>, "histogram": <time>}
* <stack> represents the hang's stack,
* ["<frame_0>", "<frame_1>", ...]
*/
[implicit_jscontext]
readonly attribute jsval threadHangStats;
/*
* An object with two fields: memoryMap and stacks.
* * memoryMap is a list of loaded libraries.

View File

@ -587,7 +587,7 @@ class nsXULAppInfo : public nsIXULAppInfo,
{
public:
nsXULAppInfo() {}
MOZ_CONSTEXPR nsXULAppInfo() {}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIXULAPPINFO
NS_DECL_NSIXULRUNTIME

View File

@ -80,7 +80,7 @@ class StackEntry : public js::ProfileEntry
{
public:
bool isCopyLabel() volatile {
bool isCopyLabel() const volatile {
return !((uintptr_t)stackAddress() & 0x1);
}

View File

@ -2690,6 +2690,7 @@ nsNativeThemeWin::ClassicThemeSupportsWidget(nsPresContext* aPresContext,
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
case NS_THEME_SCALE_HORIZONTAL:
case NS_THEME_SCALE_VERTICAL:
case NS_THEME_SCALE_THUMB_HORIZONTAL:
@ -2866,6 +2867,12 @@ nsNativeThemeWin::ClassicGetMinimumWidgetSize(nsRenderingContext* aContext, nsIF
// (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB) << 1;
break;
case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
{
aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
::GetSystemMetrics(SM_CYVSCROLL));
break;
}
case NS_THEME_RANGE_THUMB: {
if (IsRangeHorizontal(aFrame)) {
(*aResult).width = 12;

View File

@ -106,6 +106,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **)
#include "mozilla/Omnijar.h"
#include "mozilla/HangMonitor.h"
#include "mozilla/Telemetry.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "nsChromeRegistry.h"
#include "nsChromeProtocolHandler.h"
@ -587,6 +588,7 @@ NS_InitXPCOM2(nsIServiceManager* *result,
mozilla::Telemetry::Init();
mozilla::HangMonitor::Startup();
mozilla::BackgroundHangMonitor::Startup();
#ifdef MOZ_VISUAL_EVENT_TRACER
mozilla::eventtracer::Init();
@ -832,6 +834,7 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
Omnijar::CleanUp();
HangMonitor::Shutdown();
BackgroundHangMonitor::Shutdown();
#ifdef MOZ_VISUAL_EVENT_TRACER
eventtracer::Shutdown();

View File

@ -0,0 +1,490 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/BackgroundHangMonitor.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Monitor.h"
#include "mozilla/Move.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ThreadHangStats.h"
#include "mozilla/ThreadLocal.h"
#include "prinrval.h"
#include "prthread.h"
#include "ThreadStackHelper.h"
#include <algorithm>
namespace mozilla {
/**
* BackgroundHangManager is the global object that
* manages all instances of BackgroundHangThread.
*/
class BackgroundHangManager : public AtomicRefCounted<BackgroundHangManager>
{
private:
// Background hang monitor thread function
static void MonitorThread(void* aData)
{
PR_SetCurrentThreadName("BgHangManager");
/* We do not hold a reference to BackgroundHangManager here
because the monitor thread only exists as long as the
BackgroundHangManager instance exists. We stop the monitor
thread in the BackgroundHangManager destructor, and we can
only get to the destructor if we don't hold a reference here. */
static_cast<BackgroundHangManager*>(aData)->RunMonitorThread();
}
// Hang monitor thread
PRThread* mHangMonitorThread;
// Stop hang monitoring
bool mShutdown;
BackgroundHangManager(const BackgroundHangManager&);
BackgroundHangManager& operator=(const BackgroundHangManager&);
void RunMonitorThread();
public:
static StaticRefPtr<BackgroundHangManager> sInstance;
// Lock for access to members of this class
Monitor mLock;
// Current time as seen by hang monitors
PRIntervalTime mIntervalNow;
// List of BackgroundHangThread instances associated with each thread
LinkedList<BackgroundHangThread> mHangThreads;
void Shutdown()
{
MonitorAutoLock autoLock(mLock);
mShutdown = true;
autoLock.Notify();
}
void Wakeup()
{
// PR_CreateThread could have failed earlier
if (mHangMonitorThread) {
// Use PR_Interrupt to avoid potentially taking a lock
PR_Interrupt(mHangMonitorThread);
}
}
BackgroundHangManager();
~BackgroundHangManager();
};
/**
* BackgroundHangThread is a per-thread object that is used
* by all instances of BackgroundHangMonitor to monitor hangs.
*/
class BackgroundHangThread : public RefCounted<BackgroundHangThread>
, public LinkedListElement<BackgroundHangThread>
{
private:
static ThreadLocal<BackgroundHangThread*> sTlsKey;
BackgroundHangThread(const BackgroundHangThread&);
BackgroundHangThread& operator=(const BackgroundHangThread&);
/* Keep a reference to the manager, so we can keep going even
after BackgroundHangManager::Shutdown is called. */
const RefPtr<BackgroundHangManager> mManager;
// Unique thread ID for identification
const PRThread* mThreadID;
public:
static BackgroundHangThread* FindThread();
static void Startup()
{
/* We can tolerate init() failing.
The if block turns off warn_unused_result. */
if (!sTlsKey.init()) {}
}
// Hang timeout in ticks
const PRIntervalTime mTimeout;
// PermaHang timeout in ticks
const PRIntervalTime mMaxTimeout;
// Time at last activity
PRIntervalTime mInterval;
// Time when a hang started
PRIntervalTime mHangStart;
// Is the thread in a hang
bool mHanging;
// Is the thread in a waiting state
bool mWaiting;
// Platform-specific helper to get hang stacks
ThreadStackHelper mStackHelper;
// Stack of current hang
Telemetry::HangHistogram::Stack mHangStack;
// Statistics for telemetry
Telemetry::ThreadHangStats mStats;
BackgroundHangThread(const char* aName,
uint32_t aTimeoutMs,
uint32_t aMaxTimeoutMs);
~BackgroundHangThread();
// Report a hang; aManager->mLock IS locked
void ReportHang(PRIntervalTime aHangTime);
// Report a permanent hang; aManager->mLock IS locked
void ReportPermaHang() const;
// Called by BackgroundHangMonitor::NotifyActivity
void NotifyActivity();
// Called by BackgroundHangMonitor::NotifyWait
void NotifyWait()
{
NotifyActivity();
mWaiting = true;
}
};
StaticRefPtr<BackgroundHangManager> BackgroundHangManager::sInstance;
ThreadLocal<BackgroundHangThread*> BackgroundHangThread::sTlsKey;
BackgroundHangManager::BackgroundHangManager()
: mShutdown(false)
, mLock("BackgroundHangManager")
, mIntervalNow(0)
{
// Lock so we don't race against the new monitor thread
MonitorAutoLock autoLock(mLock);
mHangMonitorThread = PR_CreateThread(
PR_USER_THREAD, MonitorThread, this,
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
MOZ_ASSERT(mHangMonitorThread,
"Failed to create monitor thread");
}
BackgroundHangManager::~BackgroundHangManager()
{
MOZ_ASSERT(mShutdown,
"Destruction without Shutdown call");
MOZ_ASSERT(mHangThreads.isEmpty(),
"Destruction with outstanding monitors");
MOZ_ASSERT(mHangMonitorThread,
"No monitor thread");
// PR_CreateThread could have failed above due to resource limitation
if (mHangMonitorThread) {
// The monitor thread can only live as long as the instance lives
PR_JoinThread(mHangMonitorThread);
}
}
void
BackgroundHangManager::RunMonitorThread()
{
// Keep us locked except when waiting
MonitorAutoLock autoLock(mLock);
/* mIntervalNow is updated at various intervals determined by waitTime.
However, if an update latency is too long (due to CPU scheduling, system
sleep, etc.), we don't update mIntervalNow at all. This is done so that
long latencies in our timing are not detected as hangs. systemTime is
used to track PR_IntervalNow() and determine our latency. */
PRIntervalTime systemTime = PR_IntervalNow();
// Default values for the first iteration of thread loop
PRIntervalTime waitTime = PR_INTERVAL_NO_WAIT;
PRIntervalTime recheckTimeout = PR_INTERVAL_NO_WAIT;
while (!mShutdown) {
PR_ClearInterrupt();
nsresult rv = autoLock.Wait(waitTime);
PRIntervalTime newTime = PR_IntervalNow();
PRIntervalTime systemInterval = newTime - systemTime;
systemTime = newTime;
/* waitTime is a quarter of the shortest timeout value; If our timing
latency is low enough (less than half the shortest timeout value),
we can update mIntervalNow. */
if (MOZ_LIKELY(waitTime != PR_INTERVAL_NO_TIMEOUT &&
systemInterval < 2 * waitTime)) {
mIntervalNow += systemInterval;
}
/* If it's before the next recheck timeout, and our wait did not
get interrupted (either through Notify or PR_Interrupt), we can
keep the current waitTime and skip iterating through hang monitors. */
if (MOZ_LIKELY(systemInterval < recheckTimeout &&
systemInterval >= waitTime &&
rv == NS_OK)) {
recheckTimeout -= systemInterval;
continue;
}
/* We are in one of the following scenarios,
- Hang or permahang recheck timeout
- Thread added/removed
- Thread wait or hang ended
In all cases, we want to go through our list of hang
monitors and update waitTime and recheckTimeout. */
waitTime = PR_INTERVAL_NO_TIMEOUT;
recheckTimeout = PR_INTERVAL_NO_TIMEOUT;
// Locally hold mIntervalNow
PRIntervalTime intervalNow = mIntervalNow;
// iterate through hang monitors
for (BackgroundHangThread* currentThread = mHangThreads.getFirst();
currentThread; currentThread = currentThread->getNext()) {
if (currentThread->mWaiting) {
// Thread is waiting, not hanging
continue;
}
PRIntervalTime interval = currentThread->mInterval;
PRIntervalTime hangTime = intervalNow - interval;
if (MOZ_UNLIKELY(hangTime >= currentThread->mMaxTimeout)) {
// A permahang started
// Skip subsequent iterations and tolerate a race on mWaiting here
currentThread->mWaiting = true;
currentThread->ReportPermaHang();
continue;
}
if (MOZ_LIKELY(!currentThread->mHanging)) {
if (MOZ_UNLIKELY(hangTime >= currentThread->mTimeout)) {
// A hang started
currentThread->mStackHelper.GetStack(currentThread->mHangStack);
currentThread->mHangStart = interval;
currentThread->mHanging = true;
}
} else {
if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
// A hang ended
currentThread->ReportHang(intervalNow - currentThread->mHangStart);
currentThread->mHanging = false;
}
}
/* If we are hanging, the next time we check for hang status is when
the hang turns into a permahang. If we're not hanging, the next
recheck timeout is when we may be entering a hang. */
PRIntervalTime nextRecheck;
if (currentThread->mHanging) {
nextRecheck = currentThread->mMaxTimeout;
} else {
nextRecheck = currentThread->mTimeout;
}
recheckTimeout = std::min(recheckTimeout, nextRecheck - hangTime);
/* We wait for a quarter of the shortest timeout
value to give mIntervalNow enough granularity. */
waitTime = std::min(waitTime, currentThread->mTimeout / 4);
}
}
/* We are shutting down now.
Wait for all outstanding monitors to unregister. */
while (!mHangThreads.isEmpty()) {
autoLock.Wait(PR_INTERVAL_NO_TIMEOUT);
}
}
BackgroundHangThread::BackgroundHangThread(const char* aName,
uint32_t aTimeoutMs,
uint32_t aMaxTimeoutMs)
: mManager(BackgroundHangManager::sInstance)
, mThreadID(PR_GetCurrentThread())
, mTimeout(PR_MillisecondsToInterval(aTimeoutMs))
, mMaxTimeout(PR_MillisecondsToInterval(aMaxTimeoutMs))
, mInterval(mManager->mIntervalNow)
, mHangStart(mInterval)
, mHanging(false)
, mWaiting(true)
, mStats(aName)
{
if (sTlsKey.initialized()) {
sTlsKey.set(this);
}
// Lock here because LinkedList is not thread-safe
MonitorAutoLock autoLock(mManager->mLock);
// Add to thread list
mManager->mHangThreads.insertBack(this);
// Wake up monitor thread to process new thread
autoLock.Notify();
}
BackgroundHangThread::~BackgroundHangThread()
{
// Lock here because LinkedList is not thread-safe
MonitorAutoLock autoLock(mManager->mLock);
// Remove from thread list
remove();
// Wake up monitor thread to process removed thread
autoLock.Notify();
// We no longer have a thread
if (sTlsKey.initialized()) {
sTlsKey.set(nullptr);
}
// Move our copy of ThreadHangStats to Telemetry storage
Telemetry::RecordThreadHangStats(mStats);
}
void
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
{
// Recovered from a hang; called on the monitor thread
// mManager->mLock IS locked
Telemetry::HangHistogram newHistogram(Move(mHangStack));
for (Telemetry::HangHistogram* oldHistogram = mStats.mHangs.begin();
oldHistogram != mStats.mHangs.end(); oldHistogram++) {
if (newHistogram == *oldHistogram) {
// New histogram matches old one
oldHistogram->Add(aHangTime);
return;
}
}
// Add new histogram
newHistogram.Add(aHangTime);
mStats.mHangs.append(Move(newHistogram));
}
void
BackgroundHangThread::ReportPermaHang() const
{
// Permanently hanged; called on the monitor thread
// mManager->mLock IS locked
// TODO: Add telemetry reporting for perma-hangs
}
MOZ_ALWAYS_INLINE void
BackgroundHangThread::NotifyActivity()
{
PRIntervalTime intervalNow = mManager->mIntervalNow;
if (mWaiting) {
mInterval = intervalNow;
mWaiting = false;
/* We have to wake up the manager thread because when all threads
are waiting, the manager thread waits indefinitely as well. */
mManager->Wakeup();
} else {
PRIntervalTime duration = intervalNow - mInterval;
mStats.mActivity.Add(duration);
if (MOZ_UNLIKELY(duration >= mTimeout)) {
/* Wake up the manager thread to tell it that a hang ended */
mManager->Wakeup();
}
mInterval = intervalNow;
}
}
BackgroundHangThread*
BackgroundHangThread::FindThread()
{
if (sTlsKey.initialized()) {
// Use TLS if available
return sTlsKey.get();
}
// If TLS is unavailable, we can search through the thread list
RefPtr<BackgroundHangManager> manager(BackgroundHangManager::sInstance);
MOZ_ASSERT(manager, "Creating BackgroundHangMonitor after shutdown");
PRThread* threadID = PR_GetCurrentThread();
// Lock thread list for traversal
MonitorAutoLock autoLock(manager->mLock);
for (BackgroundHangThread* thread = manager->mHangThreads.getFirst();
thread; thread = thread->getNext()) {
if (thread->mThreadID == threadID) {
return thread;
}
}
// Current thread is not initialized
return nullptr;
}
void
BackgroundHangMonitor::Startup()
{
MOZ_ASSERT(!BackgroundHangManager::sInstance, "Already initialized");
ThreadStackHelper::Startup();
BackgroundHangThread::Startup();
BackgroundHangManager::sInstance = new BackgroundHangManager();
}
void
BackgroundHangMonitor::Shutdown()
{
MOZ_ASSERT(BackgroundHangManager::sInstance, "Not initialized");
/* Scope our lock inside Shutdown() because the sInstance object can
be destroyed as soon as we set sInstance to nullptr below, and
we don't want to hold the lock when it's being destroyed. */
BackgroundHangManager::sInstance->Shutdown();
BackgroundHangManager::sInstance = nullptr;
ThreadStackHelper::Shutdown();
}
BackgroundHangMonitor::BackgroundHangMonitor(const char* aName,
uint32_t aTimeoutMs,
uint32_t aMaxTimeoutMs)
: mThread(BackgroundHangThread::FindThread())
{
if (!mThread) {
mThread = new BackgroundHangThread(aName, aTimeoutMs, aMaxTimeoutMs);
}
}
BackgroundHangMonitor::BackgroundHangMonitor()
: mThread(BackgroundHangThread::FindThread())
{
MOZ_ASSERT(mThread, "Thread not initialized for hang monitoring");
}
BackgroundHangMonitor::~BackgroundHangMonitor()
{
}
void
BackgroundHangMonitor::NotifyActivity()
{
mThread->NotifyActivity();
}
void
BackgroundHangMonitor::NotifyWait()
{
mThread->NotifyWait();
}
/* Because we are iterating through the BackgroundHangThread linked list,
we need to take a lock. Using MonitorAutoLock as a base class makes
sure all of that is taken care of for us. */
BackgroundHangMonitor::ThreadHangStatsIterator::ThreadHangStatsIterator()
: MonitorAutoLock(BackgroundHangManager::sInstance->mLock)
, mThread(BackgroundHangManager::sInstance->mHangThreads.getFirst())
{
}
Telemetry::ThreadHangStats*
BackgroundHangMonitor::ThreadHangStatsIterator::GetNext()
{
if (!mThread) {
return nullptr;
}
Telemetry::ThreadHangStats* stats = &mThread->mStats;
mThread = mThread->getNext();
return stats;
}
} // namespace mozilla

View File

@ -0,0 +1,201 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_BackgroundHangMonitor_h
#define mozilla_BackgroundHangMonitor_h
#include "mozilla/RefPtr.h"
#include "mozilla/Monitor.h"
#include <stdint.h>
namespace mozilla {
namespace Telemetry {
class ThreadHangStats;
};
class BackgroundHangThread;
/**
* The background hang monitor is responsible for detecting and reporting
* hangs in background (non-main) threads. A thread registers itself using
* the BackgroundHangMonitor object and periodically calls its methods to
* inform the hang monitor of the thread's activity. Each thread is given
* a thread name, a timeout, and a maximum timeout. If one of the thread's
* tasks runs for longer than the timeout duration but shorter than the
* maximum timeout, a (transient) hang is reported. On the other hand, if
* a task runs for longer than the maximum timeout duration or never
* finishes (e.g. in a deadlock), a permahang is reported.
*
* Tasks are defined arbitrarily, but are typically represented by events
* in an event loop -- processing one event is equivalent to running one
* task. To ensure responsiveness, tasks in a thread often have a target
* running time. This is a good starting point for determining the timeout
* and maximum timeout values. For example, the Compositor thread has a
* responsiveness goal of 60Hz or 17ms, so a starting timeout could be
* 100ms. Considering some platforms (e.g. Android) can terminate the app
* when a critical thread hangs for longer than a few seconds, a good
* starting maximum timeout is 4 or 5 seconds.
*
* A thread registers itself through the BackgroundHangMonitor constructor.
* Multiple BackgroundHangMonitor objects can be used in one thread. The
* constructor without arguments can be used when it is known that the thread
* already has a BackgroundHangMonitor registered. When all instances of
* BackgroundHangMonitor are destroyed, the thread is unregistered.
*
* The thread then uses two methods to inform BackgroundHangMonitor of the
* thread's activity:
*
* > BackgroundHangMonitor::NotifyActivity should be called *before*
* starting a task. The task run time is determined by the interval
* between this call and the next NotifyActivity call.
*
* > BackgroundHangMonitor::NotifyWait should be called *before* the
* thread enters a wait state (e.g. to wait for a new event). This
* prevents a waiting thread from being detected as hanging. The wait
* state is automatically cleared at the next NotifyActivity call.
*
* The following example shows hang monitoring in a simple event loop:
*
* void thread_main()
* {
* mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000);
* while (!exiting) {
* hangMonitor.NotifyActivity();
* process_next_event();
* hangMonitor.NotifyWait();
* wait_for_next_event();
* }
* }
*
* The following example shows reentrancy in nested event loops:
*
* void thread_main()
* {
* mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000);
* while (!exiting) {
* hangMonitor.NotifyActivity();
* process_next_event();
* hangMonitor.NotifyWait();
* wait_for_next_event();
* }
* }
*
* void process_next_event()
* {
* mozilla::BackgroundHangMonitor hangMonitor();
* if (is_sync_event) {
* while (!finished_event) {
* hangMonitor.NotifyActivity();
* process_next_event();
* hangMonitor.NotifyWait();
* wait_for_next_event();
* }
* } else {
* process_nonsync_event();
* }
* }
*
*/
class BackgroundHangMonitor
{
private:
RefPtr<BackgroundHangThread> mThread;
public:
/**
* ThreadHangStatsIterator is used to iterate through the ThreadHangStats
* associated with each active monitored thread. Because of an internal
* lock while this object is alive, a thread must use only one instance
* of this class at a time and must iterate through the list as fast as
* possible. The following example shows using the iterator:
*
* {
* // Scope the iter variable so it's destroyed as soon as we're done
* BackgroundHangMonitor::ThreadHangStatsIterator iter;
* for (ThreadHangStats* histogram = iter.GetNext();
* histogram; histogram = iter.GetNext()) {
* // Process histogram
* }
* }
*/
class ThreadHangStatsIterator : public MonitorAutoLock
{
private:
BackgroundHangThread* mThread;
ThreadHangStatsIterator(const ThreadHangStatsIterator&);
ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&);
public:
/**
* Create an ThreadHangStatsIterator instance and take the internal lock.
* Internal lock is released on destruction.
*/
ThreadHangStatsIterator();
/**
* Get the next item in the list; the first call returns the first item.
* Returns nullptr at the end of the list.
*/
Telemetry::ThreadHangStats* GetNext();
};
/**
* Enable hang monitoring.
* Must return before using BackgroundHangMonitor.
*/
static void Startup();
/**
* Disable hang monitoring.
* Can be called without destroying all BackgroundHangMonitors first.
*/
static void Shutdown();
/**
* Start monitoring hangs for the current thread.
*
* @param aName Name to identify the thread with
* @param aTimeoutMs Amount of time in milliseconds without
* activity before registering a hang
* @param aMaxTimeoutMs Amount of time in milliseconds without
* activity before registering a permanent hang
*/
BackgroundHangMonitor(const char* aName,
uint32_t aTimeoutMs,
uint32_t aMaxTimeoutMs);
/**
* Monitor hangs using an existing monitor
* associated with the current thread.
*/
BackgroundHangMonitor();
/**
* Destroys the hang monitor; hang monitoring for a thread stops
* when all monitors associated with the thread are destroyed.
*/
~BackgroundHangMonitor();
/**
* Notify the hang monitor of pending current thread activity.
* Call this method before starting an "activity" or after
* exiting from a wait state.
*/
void NotifyActivity();
/**
* Notify the hang monitor of current thread wait.
* Call this method before entering a wait state; call
* NotifyActivity when subsequently exiting the wait state.
*/
void NotifyWait();
};
} // namespace mozilla
#endif // mozilla_BackgroundHangMonitor_h

View File

@ -0,0 +1,175 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "ThreadStackHelper.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Move.h"
#ifdef XP_LINUX
#include <unistd.h>
#include <sys/syscall.h>
#endif
#ifdef ANDROID
#ifndef SYS_gettid
#define SYS_gettid __NR_gettid
#endif
#ifndef SYS_tgkill
#define SYS_tgkill __NR_tgkill
#endif
#endif
namespace mozilla {
void
ThreadStackHelper::Startup()
{
#if defined(XP_LINUX)
MOZ_ASSERT(NS_IsMainThread());
if (!sInitialized) {
MOZ_ALWAYS_TRUE(!::sem_init(&sSem, 0, 0));
}
sInitialized++;
#endif
}
void
ThreadStackHelper::Shutdown()
{
#if defined(XP_LINUX)
MOZ_ASSERT(NS_IsMainThread());
if (sInitialized == 1) {
MOZ_ALWAYS_TRUE(!::sem_destroy(&sSem));
}
sInitialized--;
#endif
}
ThreadStackHelper::ThreadStackHelper()
: mPseudoStack(mozilla_get_pseudo_stack())
, mStackBuffer()
, mMaxStackSize(mStackBuffer.capacity())
{
#if defined(XP_LINUX)
mThreadID = ::syscall(SYS_gettid);
#elif defined(XP_WIN)
mInitialized = !!::DuplicateHandle(
::GetCurrentProcess(), ::GetCurrentThread(),
::GetCurrentProcess(), &mThreadID,
THREAD_SUSPEND_RESUME, FALSE, 0);
MOZ_ASSERT(mInitialized);
#elif defined(XP_MACOSX)
mThreadID = mach_thread_self();
#endif
}
ThreadStackHelper::~ThreadStackHelper()
{
#if defined(XP_WIN)
if (mInitialized) {
MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID));
}
#endif
}
void
ThreadStackHelper::GetStack(Stack& aStack)
{
// Always run PrepareStackBuffer first to clear aStack
if (!PrepareStackBuffer(aStack)) {
MOZ_ASSERT(false);
return;
}
#if defined(XP_LINUX)
if (!sInitialized) {
MOZ_ASSERT(false);
return;
}
sCurrent = this;
struct sigaction sigact = {};
sigact.sa_sigaction = SigAction;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_SIGINFO | SA_RESTART;
if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) {
MOZ_ASSERT(false);
return;
}
MOZ_ALWAYS_TRUE(!::syscall(SYS_tgkill, getpid(), mThreadID, SIGPROF));
MOZ_ALWAYS_TRUE(!::sem_wait(&sSem));
#elif defined(XP_WIN)
if (!mInitialized) {
MOZ_ASSERT(false);
return;
}
if (::SuspendThread(mThreadID) == DWORD(-1)) {
MOZ_ASSERT(false);
return;
}
FillStackBuffer();
MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));
#elif defined(XP_MACOSX)
if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
MOZ_ASSERT(false);
return;
}
FillStackBuffer();
MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
#endif
aStack = Move(mStackBuffer);
}
#ifdef XP_LINUX
int ThreadStackHelper::sInitialized;
sem_t ThreadStackHelper::sSem;
struct sigaction ThreadStackHelper::sOldSigAction;
ThreadStackHelper* ThreadStackHelper::sCurrent;
void
ThreadStackHelper::SigAction(int aSignal, siginfo_t* aInfo, void* aContext)
{
::sigaction(SIGPROF, &sOldSigAction, nullptr);
sCurrent->FillStackBuffer();
sCurrent = nullptr;
::sem_post(&sSem);
}
#endif // XP_LINUX
bool
ThreadStackHelper::PrepareStackBuffer(Stack& aStack) {
aStack.clear();
if (!mPseudoStack) {
return false;
}
mStackBuffer.clear();
return mStackBuffer.reserve(mMaxStackSize);
}
void
ThreadStackHelper::FillStackBuffer() {
size_t reservedSize = mMaxStackSize;
// Go from front to back
const volatile StackEntry* entry = mPseudoStack->mStack;
const volatile StackEntry* end = entry + mPseudoStack->stackSize();
for (; reservedSize-- && entry != end; entry++) {
/* We only accept non-copy labels, because
we are unable to actually copy labels here */
if (!entry->isCopyLabel()) {
mStackBuffer.infallibleAppend(entry->label());
}
}
// If we exited early due to buffer size, expand the buffer for next time
mMaxStackSize += (end - entry);
}
} // namespace mozilla

View File

@ -0,0 +1,100 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 mozilla_ThreadStackHelper_h
#define mozilla_ThreadStackHelper_h
#include "mozilla/ThreadHangStats.h"
#include "GeckoProfiler.h"
#include <stddef.h>
#if defined(XP_LINUX)
#include <signal.h>
#include <semaphore.h>
#include <sys/types.h>
#elif defined(XP_WIN)
#include <windows.h>
#elif defined(XP_MACOSX)
#include <mach/mach.h>
#endif
namespace mozilla {
/**
* ThreadStackHelper is used to retrieve the profiler pseudo-stack of a
* thread, as an alternative of using the profiler to take a profile.
* The target thread first declares an ThreadStackHelper instance;
* then another thread can call ThreadStackHelper::GetStack to retrieve
* the pseudo-stack of the target thread at that instant.
*
* Only non-copying labels are included in the stack, which means labels
* with custom text and markers are not included.
*/
class ThreadStackHelper
{
public:
typedef Telemetry::HangHistogram::Stack Stack;
private:
const PseudoStack* const mPseudoStack;
Stack mStackBuffer;
size_t mMaxStackSize;
bool PrepareStackBuffer(Stack& aStack);
void FillStackBuffer();
public:
/**
* Initialize ThreadStackHelper. Must be called from main thread.
*/
static void Startup();
/**
* Uninitialize ThreadStackHelper. Must be called from main thread.
*/
static void Shutdown();
/**
* Create a ThreadStackHelper instance targeting the current thread.
*/
ThreadStackHelper();
~ThreadStackHelper();
/**
* Retrieve the current pseudostack of the thread associated
* with this ThreadStackHelper.
*
* @param aStack Stack instance to be filled.
*/
void GetStack(Stack& aStack);
#if defined(XP_LINUX)
private:
static int sInitialized;
static sem_t sSem;
static struct sigaction sOldSigAction;
static ThreadStackHelper* sCurrent;
static void SigAction(int aSignal, siginfo_t* aInfo, void* aContext);
pid_t mThreadID;
#elif defined(XP_WIN)
private:
bool mInitialized;
HANDLE mThreadID;
#elif defined(XP_MACOSX)
private:
thread_act_t mThreadID;
#endif
};
} // namespace mozilla
#endif // mozilla_ThreadStackHelper_h

View File

@ -28,12 +28,14 @@ EXPORTS += [
]
EXPORTS.mozilla += [
'BackgroundHangMonitor.h',
'HangMonitor.h',
'LazyIdleThread.h',
'SyncRunnable.h',
]
UNIFIED_SOURCES += [
'BackgroundHangMonitor.cpp',
'HangMonitor.cpp',
'LazyIdleThread.cpp',
'nsEnvironment.cpp',
@ -44,6 +46,7 @@ UNIFIED_SOURCES += [
'nsThreadManager.cpp',
'nsThreadPool.cpp',
'nsTimerImpl.cpp',
'ThreadStackHelper.cpp',
'TimerThread.cpp',
]