зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge on a CLOSED TREE
This commit is contained in:
Коммит
a81ccbd7c4
|
@ -114,7 +114,7 @@ public:
|
|||
class Decoder
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(Decoder)
|
||||
|
||||
explicit Decoder(RasterImage* aImage);
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ private:
|
|||
class MetadataDecodingTask final : public IDecodingTask
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataDecodingTask, override)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(MetadataDecodingTask, override)
|
||||
|
||||
explicit MetadataDecodingTask(NotNull<Decoder*> aDecoder);
|
||||
|
||||
|
@ -107,7 +107,7 @@ private:
|
|||
class AnonymousDecodingTask final : public IDecodingTask
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousDecodingTask, override)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(AnonymousDecodingTask, override)
|
||||
|
||||
explicit AnonymousDecodingTask(NotNull<Decoder*> aDecoder,
|
||||
bool aResumable);
|
||||
|
|
|
@ -635,6 +635,7 @@ js::Nursery::renderProfileJSON(JSONPrinter& json) const
|
|||
json.property("reason", JS::gcreason::ExplainReason(previousGC.reason));
|
||||
json.property("bytes_tenured", previousGC.tenuredBytes);
|
||||
json.property("cells_tenured", previousGC.tenuredCells);
|
||||
json.property("strings_tenured", stats().getStat(gcstats::STAT_STRINGS_TENURED));
|
||||
json.property("bytes_used", previousGC.nurseryUsedBytes);
|
||||
json.property("cur_capacity", previousGC.nurseryCapacity);
|
||||
const size_t newCapacity = spaceToEnd(maxChunkCount());
|
||||
|
@ -661,6 +662,10 @@ js::Nursery::renderProfileJSON(JSONPrinter& json) const
|
|||
json.property("groups_pretenured",
|
||||
stats().getStat(gcstats::STAT_OBJECT_GROUPS_PRETENURED));
|
||||
}
|
||||
if (stats().getStat(gcstats::STAT_NURSERY_STRING_REALMS_DISABLED)) {
|
||||
json.property("nursery_string_realms_disabled",
|
||||
stats().getStat(gcstats::STAT_NURSERY_STRING_REALMS_DISABLED));
|
||||
}
|
||||
|
||||
json.beginObjectProperty("phase_times");
|
||||
|
||||
|
@ -833,6 +838,8 @@ js::Nursery::collect(JS::gcreason::Reason reason)
|
|||
stats().setStat(gcstats::STAT_OBJECT_GROUPS_PRETENURED, pretenureCount);
|
||||
|
||||
mozilla::Maybe<AutoGCSession> session;
|
||||
uint32_t numStringsTenured = 0;
|
||||
uint32_t numNurseryStringRealmsDisabled = 0;
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
if (shouldPretenure && zone->allocNurseryStrings && zone->tenuredStrings >= 30 * 1000) {
|
||||
if (!session.isSome()) {
|
||||
|
@ -847,13 +854,17 @@ js::Nursery::collect(JS::gcreason::Reason reason)
|
|||
if (jit::JitRealm* jitRealm = r->jitRealm()) {
|
||||
jitRealm->discardStubs();
|
||||
jitRealm->stringsCanBeInNursery = false;
|
||||
numNurseryStringRealmsDisabled++;
|
||||
}
|
||||
}
|
||||
zone->allocNurseryStrings = false;
|
||||
}
|
||||
numStringsTenured += zone->tenuredStrings;
|
||||
zone->tenuredStrings = 0;
|
||||
}
|
||||
session.reset(); // End the minor GC session, if running one.
|
||||
stats().setStat(gcstats::STAT_NURSERY_STRING_REALMS_DISABLED, numNurseryStringRealmsDisabled);
|
||||
stats().setStat(gcstats::STAT_STRINGS_TENURED, numStringsTenured);
|
||||
endProfile(ProfileKey::Pretenure);
|
||||
|
||||
// We ignore gcMaxBytes when allocating for minor collection. However, if we
|
||||
|
|
|
@ -50,9 +50,15 @@ enum Count {
|
|||
|
||||
// Stats can be set with Statistics::setStat(). They're not reset automatically.
|
||||
enum Stat {
|
||||
// Number of strings tenured.
|
||||
STAT_STRINGS_TENURED,
|
||||
|
||||
// Number of object types pretenured this minor GC.
|
||||
STAT_OBJECT_GROUPS_PRETENURED,
|
||||
|
||||
// Number of realms that had nursery strings disabled due to large numbers being tenured.
|
||||
STAT_NURSERY_STRING_REALMS_DISABLED,
|
||||
|
||||
STAT_LIMIT
|
||||
};
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ jittest:
|
|||
by-test-platform:
|
||||
android-hw.*: ['try']
|
||||
linux.*: [] # redundant with SM(...)
|
||||
windows.*: [] # redundant with SM(p)
|
||||
default: built-projects
|
||||
chunks:
|
||||
by-test-platform:
|
||||
|
|
|
@ -159,8 +159,17 @@ Stream::RecordOrReplayThreadEvent(ThreadEvent aEvent)
|
|||
} else {
|
||||
ThreadEvent oldEvent = (ThreadEvent) ReadScalar();
|
||||
if (oldEvent != aEvent) {
|
||||
child::ReportFatalError(Nothing(), "Event Mismatch: Recorded %s Replayed %s",
|
||||
ThreadEventName(oldEvent), ThreadEventName(aEvent));
|
||||
const char* extra = "";
|
||||
if (oldEvent == ThreadEvent::Assert) {
|
||||
// Include the asserted string in the error. This must match up with
|
||||
// the writes in RecordReplayAssert.
|
||||
if (mNameIndex == MainThreadId) {
|
||||
(void) ReadScalar(); // For the ExecutionProgressCounter write below.
|
||||
}
|
||||
extra = ReadInputString();
|
||||
}
|
||||
child::ReportFatalError(Nothing(), "Event Mismatch: Recorded %s %s Replayed %s",
|
||||
ThreadEventName(oldEvent), extra, ThreadEventName(aEvent));
|
||||
}
|
||||
mLastEvent = aEvent;
|
||||
}
|
||||
|
@ -185,6 +194,16 @@ Stream::CheckInput(size_t aValue)
|
|||
}
|
||||
}
|
||||
|
||||
const char*
|
||||
Stream::ReadInputString()
|
||||
{
|
||||
size_t len = ReadScalar();
|
||||
EnsureInputBallast(len + 1);
|
||||
ReadBytes(mInputBallast.get(), len);
|
||||
mInputBallast[len] = 0;
|
||||
return mInputBallast.get();
|
||||
}
|
||||
|
||||
void
|
||||
Stream::CheckInput(const char* aValue)
|
||||
{
|
||||
|
@ -193,14 +212,10 @@ Stream::CheckInput(const char* aValue)
|
|||
WriteScalar(len);
|
||||
WriteBytes(aValue, len);
|
||||
} else {
|
||||
size_t oldLen = ReadScalar();
|
||||
EnsureInputBallast(oldLen + 1);
|
||||
ReadBytes(mInputBallast.get(), oldLen);
|
||||
mInputBallast[oldLen] = 0;
|
||||
|
||||
if (len != oldLen || memcmp(aValue, mInputBallast.get(), len) != 0) {
|
||||
const char* oldInput = ReadInputString();
|
||||
if (strcmp(oldInput, aValue) != 0) {
|
||||
child::ReportFatalError(Nothing(), "Input Mismatch: %s Recorded %s Replayed %s",
|
||||
ThreadEventName(mLastEvent), mInputBallast.get(), aValue);
|
||||
ThreadEventName(mLastEvent), oldInput, aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,6 +197,7 @@ private:
|
|||
ShouldCopy aCopy);
|
||||
void EnsureInputBallast(size_t aSize);
|
||||
void Flush(bool aTakeLock);
|
||||
const char* ReadInputString();
|
||||
|
||||
static size_t BallastMaxSize();
|
||||
};
|
||||
|
|
|
@ -44,6 +44,8 @@ GatherDependentCalls(InfallibleVector<MiddlemanCall*>& aOutgoingCalls, Middleman
|
|||
MOZ_RELEASE_ASSERT(!aCall->mSent);
|
||||
aCall->mSent = true;
|
||||
|
||||
const Redirection& redirection = GetRedirection(aCall->mCallId);
|
||||
|
||||
CallArguments arguments;
|
||||
aCall->mArguments.CopyTo(&arguments);
|
||||
|
||||
|
@ -51,8 +53,12 @@ GatherDependentCalls(InfallibleVector<MiddlemanCall*>& aOutgoingCalls, Middleman
|
|||
|
||||
MiddlemanCallContext cx(aCall, &arguments, MiddlemanCallPhase::ReplayInput);
|
||||
cx.mDependentCalls = &dependentCalls;
|
||||
gRedirections[aCall->mCallId].mMiddlemanCall(cx);
|
||||
redirection.mMiddlemanCall(cx);
|
||||
if (cx.mFailed) {
|
||||
if (child::CurrentRepaintCannotFail()) {
|
||||
child::ReportFatalError(Nothing(), "Middleman call input failed: %s\n",
|
||||
redirection.mName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -71,7 +77,7 @@ SendCallToMiddleman(size_t aCallId, CallArguments* aArguments, bool aDiverged)
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(IsReplaying());
|
||||
|
||||
const Redirection& redirection = gRedirections[aCallId];
|
||||
const Redirection& redirection = GetRedirection(aCallId);
|
||||
MOZ_RELEASE_ASSERT(redirection.mMiddlemanCall);
|
||||
|
||||
MonitorAutoLock lock(*gMonitor);
|
||||
|
@ -91,6 +97,10 @@ SendCallToMiddleman(size_t aCallId, CallArguments* aArguments, bool aDiverged)
|
|||
if (cx.mFailed) {
|
||||
delete newCall;
|
||||
gMiddlemanCalls.popBack();
|
||||
if (child::CurrentRepaintCannotFail()) {
|
||||
child::ReportFatalError(Nothing(), "Middleman call preface failed: %s\n",
|
||||
redirection.mName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +142,7 @@ SendCallToMiddleman(size_t aCallId, CallArguments* aArguments, bool aDiverged)
|
|||
call->mArguments.CopyTo(&oldArguments);
|
||||
MiddlemanCallContext cx(call, &oldArguments, MiddlemanCallPhase::ReplayOutput);
|
||||
cx.mReplayOutputIsOld = true;
|
||||
gRedirections[call->mCallId].mMiddlemanCall(cx);
|
||||
GetRedirection(call->mCallId).mMiddlemanCall(cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +166,7 @@ ProcessMiddlemanCall(const char* aInputData, size_t aInputSize,
|
|||
MiddlemanCall* call = new MiddlemanCall();
|
||||
call->DecodeInput(inputStream);
|
||||
|
||||
const Redirection& redirection = gRedirections[call->mCallId];
|
||||
const Redirection& redirection = GetRedirection(call->mCallId);
|
||||
MOZ_RELEASE_ASSERT(redirection.mMiddlemanCall);
|
||||
|
||||
CallArguments arguments;
|
||||
|
@ -167,7 +177,7 @@ ProcessMiddlemanCall(const char* aInputData, size_t aInputSize,
|
|||
redirection.mMiddlemanCall(cx);
|
||||
}
|
||||
|
||||
RecordReplayInvokeCall(call->mCallId, &arguments);
|
||||
RecordReplayInvokeCall(redirection.mBaseFunction, &arguments);
|
||||
|
||||
{
|
||||
MiddlemanCallContext cx(call, &arguments, MiddlemanCallPhase::MiddlemanOutput);
|
||||
|
@ -213,17 +223,22 @@ ResetMiddlemanCalls()
|
|||
call->mArguments.CopyTo(&arguments);
|
||||
|
||||
MiddlemanCallContext cx(call, &arguments, MiddlemanCallPhase::MiddlemanRelease);
|
||||
gRedirections[call->mCallId].mMiddlemanCall(cx);
|
||||
|
||||
delete call;
|
||||
GetRedirection(call->mCallId).mMiddlemanCall(cx);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the calls in a second pass. The MiddlemanRelease phase depends on
|
||||
// previous middleman calls still existing.
|
||||
for (MiddlemanCall* call : gMiddlemanCalls) {
|
||||
delete call;
|
||||
}
|
||||
|
||||
gMiddlemanCalls.clear();
|
||||
for (auto buffer : gAllocatedBuffers) {
|
||||
free(buffer);
|
||||
}
|
||||
gAllocatedBuffers.clear();
|
||||
gMiddlemanCallMap->clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -258,7 +273,7 @@ GetMiddlemanCallValue(size_t aId)
|
|||
}
|
||||
|
||||
bool
|
||||
Middleman_SystemInput(MiddlemanCallContext& aCx, const void** aThingPtr)
|
||||
MM_SystemInput(MiddlemanCallContext& aCx, const void** aThingPtr)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aCx.AccessPreface());
|
||||
|
||||
|
@ -314,7 +329,7 @@ MangleSystemValue(const void* aValue, bool aFromRecording)
|
|||
}
|
||||
|
||||
void
|
||||
Middleman_SystemOutput(MiddlemanCallContext& aCx, const void** aOutput, bool aUpdating)
|
||||
MM_SystemOutput(MiddlemanCallContext& aCx, const void** aOutput, bool aUpdating)
|
||||
{
|
||||
if (!*aOutput) {
|
||||
if (aCx.mPhase == MiddlemanCallPhase::MiddlemanOutput) {
|
||||
|
|
|
@ -356,7 +356,7 @@ void ResetMiddlemanCalls();
|
|||
// Capture the contents of an input buffer at BufferArg with element count at CountArg.
|
||||
template <size_t BufferArg, size_t CountArg, typename ElemType = char>
|
||||
static inline void
|
||||
Middleman_Buffer(MiddlemanCallContext& aCx)
|
||||
MM_Buffer(MiddlemanCallContext& aCx)
|
||||
{
|
||||
if (aCx.AccessPreface()) {
|
||||
auto& buffer = aCx.mArguments->Arg<BufferArg, void*>();
|
||||
|
@ -368,7 +368,7 @@ Middleman_Buffer(MiddlemanCallContext& aCx)
|
|||
// Capture the contents of a fixed size input buffer.
|
||||
template <size_t BufferArg, size_t ByteSize>
|
||||
static inline void
|
||||
Middleman_BufferFixedSize(MiddlemanCallContext& aCx)
|
||||
MM_BufferFixedSize(MiddlemanCallContext& aCx)
|
||||
{
|
||||
if (aCx.AccessPreface()) {
|
||||
auto& buffer = aCx.mArguments->Arg<BufferArg, void*>();
|
||||
|
@ -381,7 +381,7 @@ Middleman_BufferFixedSize(MiddlemanCallContext& aCx)
|
|||
// Capture a C string argument.
|
||||
template <size_t StringArg>
|
||||
static inline void
|
||||
Middleman_CString(MiddlemanCallContext& aCx)
|
||||
MM_CString(MiddlemanCallContext& aCx)
|
||||
{
|
||||
if (aCx.AccessPreface()) {
|
||||
auto& buffer = aCx.mArguments->Arg<StringArg, char*>();
|
||||
|
@ -394,7 +394,7 @@ Middleman_CString(MiddlemanCallContext& aCx)
|
|||
// Capture the data written to an output buffer at BufferArg with element count at CountArg.
|
||||
template <size_t BufferArg, size_t CountArg, typename ElemType>
|
||||
static inline void
|
||||
Middleman_WriteBuffer(MiddlemanCallContext& aCx)
|
||||
MM_WriteBuffer(MiddlemanCallContext& aCx)
|
||||
{
|
||||
auto& buffer = aCx.mArguments->Arg<BufferArg, void*>();
|
||||
auto count = aCx.mArguments->Arg<CountArg, size_t>();
|
||||
|
@ -404,7 +404,7 @@ Middleman_WriteBuffer(MiddlemanCallContext& aCx)
|
|||
// Capture the data written to a fixed size output buffer.
|
||||
template <size_t BufferArg, size_t ByteSize>
|
||||
static inline void
|
||||
Middleman_WriteBufferFixedSize(MiddlemanCallContext& aCx)
|
||||
MM_WriteBufferFixedSize(MiddlemanCallContext& aCx)
|
||||
{
|
||||
auto& buffer = aCx.mArguments->Arg<BufferArg, void*>();
|
||||
aCx.ReadOrWriteOutputBuffer(&buffer, ByteSize);
|
||||
|
@ -413,15 +413,15 @@ Middleman_WriteBufferFixedSize(MiddlemanCallContext& aCx)
|
|||
// Capture return values that are too large for register storage.
|
||||
template <size_t ByteSize>
|
||||
static inline void
|
||||
Middleman_OversizeRval(MiddlemanCallContext& aCx)
|
||||
MM_OversizeRval(MiddlemanCallContext& aCx)
|
||||
{
|
||||
Middleman_WriteBufferFixedSize<0, ByteSize>(aCx);
|
||||
MM_WriteBufferFixedSize<0, ByteSize>(aCx);
|
||||
}
|
||||
|
||||
// Capture a byte count of stack argument data.
|
||||
template <size_t ByteSize>
|
||||
static inline void
|
||||
Middleman_StackArgumentData(MiddlemanCallContext& aCx)
|
||||
MM_StackArgumentData(MiddlemanCallContext& aCx)
|
||||
{
|
||||
if (aCx.AccessPreface()) {
|
||||
auto stack = aCx.mArguments->StackAddress<0>();
|
||||
|
@ -430,17 +430,17 @@ Middleman_StackArgumentData(MiddlemanCallContext& aCx)
|
|||
}
|
||||
|
||||
static inline void
|
||||
Middleman_NoOp(MiddlemanCallContext& aCx)
|
||||
MM_NoOp(MiddlemanCallContext& aCx)
|
||||
{
|
||||
}
|
||||
|
||||
template <MiddlemanCallFn Fn0,
|
||||
MiddlemanCallFn Fn1,
|
||||
MiddlemanCallFn Fn2 = Middleman_NoOp,
|
||||
MiddlemanCallFn Fn3 = Middleman_NoOp,
|
||||
MiddlemanCallFn Fn4 = Middleman_NoOp>
|
||||
MiddlemanCallFn Fn2 = MM_NoOp,
|
||||
MiddlemanCallFn Fn3 = MM_NoOp,
|
||||
MiddlemanCallFn Fn4 = MM_NoOp>
|
||||
static inline void
|
||||
Middleman_Compose(MiddlemanCallContext& aCx)
|
||||
MM_Compose(MiddlemanCallContext& aCx)
|
||||
{
|
||||
Fn0(aCx);
|
||||
Fn1(aCx);
|
||||
|
@ -452,11 +452,11 @@ Middleman_Compose(MiddlemanCallContext& aCx)
|
|||
// Helper for capturing inputs that are produced by other middleman calls.
|
||||
// Returns false in the ReplayInput or MiddlemanInput phases if the input
|
||||
// system value could not be found.
|
||||
bool Middleman_SystemInput(MiddlemanCallContext& aCx, const void** aThingPtr);
|
||||
bool MM_SystemInput(MiddlemanCallContext& aCx, const void** aThingPtr);
|
||||
|
||||
// Helper for capturing output system values that might be consumed by other
|
||||
// middleman calls.
|
||||
void Middleman_SystemOutput(MiddlemanCallContext& aCx, const void** aOutput, bool aUpdating = false);
|
||||
void MM_SystemOutput(MiddlemanCallContext& aCx, const void** aOutput, bool aUpdating = false);
|
||||
|
||||
} // namespace recordreplay
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -134,6 +134,7 @@ RecordReplayInterface_Initialize(int aArgc, char* aArgv[])
|
|||
fprintf(stderr, "Initialization Failure: %s\n", gInitializationFailureMessage);
|
||||
}
|
||||
|
||||
LateInitializeRedirections();
|
||||
Thread::InitializeThreads();
|
||||
|
||||
Thread* thread = Thread::GetById(MainThreadId);
|
||||
|
@ -326,7 +327,7 @@ ThreadEventName(ThreadEvent aEvent)
|
|||
case ThreadEvent::CallStart: break;
|
||||
}
|
||||
size_t callId = (size_t) aEvent - (size_t) ThreadEvent::CallStart;
|
||||
return gRedirections[callId].mName;
|
||||
return GetRedirection(callId).mName;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -354,6 +355,8 @@ RecordReplayInterface_InternalRecordReplayAssert(const char* aFormat, va_list aA
|
|||
char text[1024];
|
||||
VsprintfLiteral(text, aFormat, aArgs);
|
||||
|
||||
// This must be kept in sync with Stream::RecordOrReplayThreadEvent, which
|
||||
// peeks at the input string written after the thread event.
|
||||
thread->Events().RecordOrReplayThreadEvent(ThreadEvent::Assert);
|
||||
thread->Events().CheckInput(text);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ CallPreambleHook(PreambleFn aPreamble, size_t aCallId, CallArguments* aArguments
|
|||
return true;
|
||||
case PreambleResult::PassThrough: {
|
||||
AutoEnsurePassThroughThreadEvents pt;
|
||||
RecordReplayInvokeCall(aCallId, aArguments);
|
||||
RecordReplayInvokeCall(OriginalFunction(aCallId), aArguments);
|
||||
return true;
|
||||
}
|
||||
case PreambleResult::Redirect:
|
||||
|
@ -53,7 +53,7 @@ extern "C" {
|
|||
__attribute__((used)) int
|
||||
RecordReplayInterceptCall(int aCallId, CallArguments* aArguments)
|
||||
{
|
||||
Redirection& redirection = gRedirections[aCallId];
|
||||
Redirection& redirection = GetRedirection(aCallId);
|
||||
|
||||
// Call the preamble to see if this call needs special handling, even when
|
||||
// events have been passed through.
|
||||
|
@ -120,7 +120,7 @@ RecordReplayInterceptCall(int aCallId, CallArguments* aArguments)
|
|||
// from being flushed in case we end up blocking.
|
||||
res.reset();
|
||||
thread->SetPassThrough(true);
|
||||
RecordReplayInvokeCall(aCallId, aArguments);
|
||||
RecordReplayInvokeCall(redirection.mOriginalFunction, aArguments);
|
||||
thread->SetPassThrough(false);
|
||||
res.emplace(thread);
|
||||
}
|
||||
|
@ -287,9 +287,9 @@ __asm(
|
|||
} // extern "C"
|
||||
|
||||
MOZ_NEVER_INLINE void
|
||||
RecordReplayInvokeCall(size_t aCallId, CallArguments* aArguments)
|
||||
RecordReplayInvokeCall(void* aFunction, CallArguments* aArguments)
|
||||
{
|
||||
RecordReplayInvokeCallRaw(aArguments, OriginalFunction(aCallId));
|
||||
RecordReplayInvokeCallRaw(aArguments, aFunction);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -738,22 +738,6 @@ CopyInstructions(const char* aName, uint8_t* aIpStart, uint8_t* aIpEnd,
|
|||
return ip;
|
||||
}
|
||||
|
||||
// Get the instruction pointer to use as the address of the base function for a
|
||||
// redirection.
|
||||
static uint8_t*
|
||||
FunctionStartAddress(Redirection& aRedirection)
|
||||
{
|
||||
uint8_t* addr = static_cast<uint8_t*>(dlsym(RTLD_DEFAULT, aRedirection.mName));
|
||||
if (!addr)
|
||||
return nullptr;
|
||||
|
||||
if (addr[0] == 0xFF && addr[1] == 0x25) {
|
||||
return *(uint8_t**)(addr + 6 + *reinterpret_cast<int32_t*>(addr + 2));
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
// Generate code to set %rax and enter RecordReplayRedirectCall.
|
||||
static uint8_t*
|
||||
GenerateRedirectStub(Assembler& aAssembler, size_t aCallId)
|
||||
|
@ -909,34 +893,6 @@ Redirect(size_t aCallId, Redirection& aRedirection, Assembler& aAssembler, bool
|
|||
AddClobberPatch(functionStart + ShortJumpBytes, nro);
|
||||
}
|
||||
|
||||
void
|
||||
EarlyInitializeRedirections()
|
||||
{
|
||||
for (size_t i = 0;; i++) {
|
||||
Redirection& redirection = gRedirections[i];
|
||||
if (!redirection.mName) {
|
||||
break;
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(!redirection.mBaseFunction);
|
||||
MOZ_RELEASE_ASSERT(!redirection.mOriginalFunction);
|
||||
|
||||
redirection.mBaseFunction = FunctionStartAddress(redirection);
|
||||
redirection.mOriginalFunction = redirection.mBaseFunction;
|
||||
|
||||
if (redirection.mBaseFunction && IsRecordingOrReplaying()) {
|
||||
// We will get confused if we try to redirect the same address in multiple places.
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
if (gRedirections[j].mBaseFunction == redirection.mBaseFunction) {
|
||||
PrintSpew("Redirection %s shares the same address as %s, skipping.\n",
|
||||
redirection.mName, gRedirections[j].mName);
|
||||
redirection.mBaseFunction = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
InitializeRedirections()
|
||||
{
|
||||
|
@ -944,20 +900,15 @@ InitializeRedirections()
|
|||
|
||||
{
|
||||
Assembler assembler;
|
||||
size_t numRedirections = NumRedirections();
|
||||
|
||||
for (size_t i = 0;; i++) {
|
||||
Redirection& redirection = gRedirections[i];
|
||||
if (!redirection.mName) {
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < numRedirections; i++) {
|
||||
Redirection& redirection = GetRedirection(i);
|
||||
Redirect(i, redirection, assembler, /* aFirstPass = */ true);
|
||||
}
|
||||
|
||||
for (size_t i = 0;; i++) {
|
||||
Redirection& redirection = gRedirections[i];
|
||||
if (!redirection.mName) {
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < numRedirections; i++) {
|
||||
Redirection& redirection = GetRedirection(i);
|
||||
Redirect(i, redirection, assembler, /* aFirstPass = */ false);
|
||||
}
|
||||
}
|
||||
|
@ -1002,6 +953,19 @@ InitializeRedirections()
|
|||
return true;
|
||||
}
|
||||
|
||||
void*
|
||||
OriginalFunction(const char* aName)
|
||||
{
|
||||
size_t numRedirections = NumRedirections();
|
||||
for (size_t i = 0; i < numRedirections; i++) {
|
||||
const Redirection& redirection = GetRedirection(i);
|
||||
if (!strcmp(aName, redirection.mName)) {
|
||||
return redirection.mOriginalFunction;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("OriginalFunction: unknown redirection");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Utility
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -114,27 +114,35 @@ public:
|
|||
// expected, per the requirements of the System V x64 ABI.
|
||||
struct CallArguments : public CallRegisterArguments
|
||||
{
|
||||
// The maximum number of stack arguments that can be captured.
|
||||
static const size_t NumStackArguments = 64;
|
||||
|
||||
protected:
|
||||
size_t stack[64]; // 104
|
||||
// Size: 616
|
||||
size_t stack[NumStackArguments]; // 104
|
||||
// Size: 616
|
||||
|
||||
public:
|
||||
template <size_t Index, typename T>
|
||||
T& Arg() {
|
||||
template <typename T>
|
||||
T& Arg(size_t aIndex) {
|
||||
static_assert(sizeof(T) == sizeof(size_t), "Size must match");
|
||||
static_assert(IsFloatingPoint<T>::value == false, "FloatArg NYI");
|
||||
static_assert(Index < 70, "Bad index");
|
||||
switch (Index) {
|
||||
MOZ_RELEASE_ASSERT(aIndex < 70);
|
||||
switch (aIndex) {
|
||||
case 0: return (T&)arg0;
|
||||
case 1: return (T&)arg1;
|
||||
case 2: return (T&)arg2;
|
||||
case 3: return (T&)arg3;
|
||||
case 4: return (T&)arg4;
|
||||
case 5: return (T&)arg5;
|
||||
default: return (T&)stack[Index - 6];
|
||||
default: return (T&)stack[aIndex - 6];
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t Index, typename T>
|
||||
T& Arg() {
|
||||
return Arg<T>(Index);
|
||||
}
|
||||
|
||||
template <size_t Offset>
|
||||
size_t* StackAddress() {
|
||||
static_assert(Offset % sizeof(size_t) == 0, "Bad stack offset");
|
||||
|
@ -248,10 +256,11 @@ struct Redirection
|
|||
PreambleFn mMiddlemanPreamble;
|
||||
};
|
||||
|
||||
// All platform specific redirections, indexed by the call event.
|
||||
extern Redirection gRedirections[];
|
||||
// Platform specific methods describing the set of redirections.
|
||||
size_t NumRedirections();
|
||||
Redirection& GetRedirection(size_t aCallId);
|
||||
|
||||
// Do early initialization of redirections. This is done on both
|
||||
// Platform specific early initialization of redirections. This is done on both
|
||||
// recording/replaying and middleman processes, and allows OriginalCall() to
|
||||
// work in either case.
|
||||
void EarlyInitializeRedirections();
|
||||
|
@ -260,6 +269,10 @@ void EarlyInitializeRedirections();
|
|||
// gInitializationFailureMessage.
|
||||
bool InitializeRedirections();
|
||||
|
||||
// Platform specific function called after setting up redirections in recording
|
||||
// or replaying processes.
|
||||
void LateInitializeRedirections();
|
||||
|
||||
// Functions for saving or restoring system error codes.
|
||||
static inline ErrorType SaveError() { return errno; }
|
||||
static inline void RestoreError(ErrorType aError) { errno = aError; }
|
||||
|
@ -274,20 +287,11 @@ DefineAllCallFunctions(DEFAULTABI)
|
|||
static inline void*
|
||||
OriginalFunction(size_t aCallId)
|
||||
{
|
||||
return gRedirections[aCallId].mOriginalFunction;
|
||||
return GetRedirection(aCallId).mOriginalFunction;
|
||||
}
|
||||
|
||||
#define TokenPaste(aFirst, aSecond) aFirst ## aSecond
|
||||
|
||||
// Call the original function for a call event ID with a particular ABI and any
|
||||
// number of arguments.
|
||||
#define OriginalCallABI(aName, aReturnType, aABI, ...) \
|
||||
TokenPaste(CallFunction, aABI) <aReturnType> \
|
||||
(OriginalFunction(CallEvent_ ##aName), ##__VA_ARGS__)
|
||||
|
||||
// Call the original function for a call event ID with the default ABI.
|
||||
#define OriginalCall(aName, aReturnType, ...) \
|
||||
OriginalCallABI(aName, aReturnType, DEFAULTABI, ##__VA_ARGS__)
|
||||
// Get the address of the original function by name.
|
||||
void* OriginalFunction(const char* aName);
|
||||
|
||||
static inline ThreadEvent
|
||||
CallIdToThreadEvent(size_t aCallId)
|
||||
|
@ -296,7 +300,7 @@ CallIdToThreadEvent(size_t aCallId)
|
|||
}
|
||||
|
||||
void
|
||||
RecordReplayInvokeCall(size_t aCallId, CallArguments* aArguments);
|
||||
RecordReplayInvokeCall(void* aFunction, CallArguments* aArguments);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Callback Redirections
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -611,7 +611,7 @@ public:
|
|||
#define NS_INLINE_DECL_REFCOUNTING(_class, ...) \
|
||||
NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, delete(this), __VA_ARGS__)
|
||||
|
||||
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, _decl, ...) \
|
||||
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, _decl, _recording, ...) \
|
||||
public: \
|
||||
_decl(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
|
||||
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
|
||||
|
@ -632,7 +632,7 @@ public: \
|
|||
} \
|
||||
typedef mozilla::TrueType HasThreadSafeRefCnt; \
|
||||
protected: \
|
||||
::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
|
||||
::mozilla::ThreadSafeAutoRefCntWithRecording<_recording> mRefCnt; \
|
||||
public:
|
||||
|
||||
/**
|
||||
|
@ -644,14 +644,36 @@ public:
|
|||
* @param _class The name of the class implementing the method
|
||||
*/
|
||||
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING(_class, ...) \
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_METHOD_, __VA_ARGS__)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_METHOD_, \
|
||||
mozilla::recordreplay::Behavior::DontPreserve, \
|
||||
__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Like NS_INLINE_DECL_THREADSAFE_REFCOUNTING with AddRef & Release declared
|
||||
* virtual.
|
||||
*/
|
||||
#define NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(_class, ...) \
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_IMETHOD_, __VA_ARGS__)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_IMETHOD_, \
|
||||
mozilla::recordreplay::Behavior::DontPreserve, \
|
||||
__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Like NS_INLINE_DECL_THREADSAFE_REFCOUNTING except that reference changes
|
||||
* are recorded and replayed.
|
||||
*/
|
||||
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_RECORDED(_class, ...) \
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_METHOD_, \
|
||||
mozilla::recordreplay::Behavior::Preserve, \
|
||||
__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Like NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING except that reference
|
||||
* changes are recorded and replayed.
|
||||
*/
|
||||
#define NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING_RECORDED(_class, ...) \
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_IMETHOD_, \
|
||||
mozilla::recordreplay::Behavior::Preserve, \
|
||||
__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Use this macro in interface classes that you want to be able to reference
|
||||
|
|
Загрузка…
Ссылка в новой задаче