Bug 1491067 - Templatize and common up redirections code, r=froydnj.

--HG--
extra : rebase_source : e08b130f57c1013ebb40c0d2cc2145947bb63fc6
This commit is contained in:
Brian Hackett 2018-09-24 11:10:25 -10:00
Родитель 3d58a9d22a
Коммит c5a6b03d14
3 изменённых файлов: 1400 добавлений и 2628 удалений

Просмотреть файл

@ -23,6 +23,218 @@ namespace {
namespace mozilla {
namespace recordreplay {
///////////////////////////////////////////////////////////////////////////////
// Redirection Skeleton
///////////////////////////////////////////////////////////////////////////////
extern "C" {
__attribute__((used)) int
RecordReplayInterceptCall(int aCallId, CallArguments* aArguments)
{
Redirection& redirection = gRedirections[aCallId];
// Call the preamble to see if this call needs special handling, even when
// events have been passed through.
if (redirection.mPreamble) {
PreambleResult result = redirection.mPreamble(aArguments);
switch (result) {
case PreambleResult::Veto:
return 0;
case PreambleResult::PassThrough: {
AutoEnsurePassThroughThreadEvents pt;
RecordReplayInvokeCall(aCallId, aArguments);
return 0;
}
case PreambleResult::Redirect:
break;
}
}
Thread* thread = Thread::Current();
// When events are passed through, invoke the call with the original stack
// and register state.
if (!thread || thread->PassThroughEvents()) {
// RecordReplayRedirectCall will load the function to call from the
// return value slot.
aArguments->Rval<uint8_t*>() = redirection.mOriginalFunction;
return 1;
}
// Calling any redirection which performs the standard steps will cause
// debugger operations that have diverged from the recording to fail.
EnsureNotDivergedFromRecording();
MOZ_RELEASE_ASSERT(thread->CanAccessRecording());
if (IsRecording()) {
// Call the original function, passing through events while we do so.
thread->SetPassThrough(true);
RecordReplayInvokeCall(aCallId, aArguments);
thread->SetPassThrough(false);
}
// Save any system error in case we want to record/replay it.
ErrorType error = SaveError();
// Add an event for the thread.
thread->Events().RecordOrReplayThreadEvent(CallIdToThreadEvent(aCallId));
// Save any output produced by the call.
if (redirection.mSaveOutput) {
redirection.mSaveOutput(thread->Events(), aArguments, &error);
}
RestoreError(error);
return 0;
}
// Entry point for all redirections. When generated code jumps here, %rax holds
// the CallEvent being invoked, and all other registers and stack contents are
// the same as when the call was originally invoked. This fills in a
// CallArguments structure with information about the call, before invoking
// RecordReplayInterceptCall.
extern size_t
RecordReplayRedirectCall(...);
__asm(
"_RecordReplayRedirectCall:"
// Make space for a CallArguments struct on the stack.
"subq $616, %rsp;"
// Fill in the structure's contents.
"movq %rdi, 0(%rsp);"
"movq %rsi, 8(%rsp);"
"movq %rdx, 16(%rsp);"
"movq %rcx, 24(%rsp);"
"movq %r8, 32(%rsp);"
"movq %r9, 40(%rsp);"
"movsd %xmm0, 48(%rsp);"
"movsd %xmm1, 56(%rsp);"
"movsd %xmm2, 64(%rsp);"
// Count how many stack arguments we need to save.
"movq $64, %rsi;"
// Enter the loop below. The compiler might not place this block of code
// adjacent to the loop, so perform the jump explicitly.
"jmp _RecordReplayRedirectCall_Loop;"
// Save stack arguments into the structure.
"_RecordReplayRedirectCall_Loop:"
"subq $1, %rsi;"
"movq 624(%rsp, %rsi, 8), %rdx;" // Ignore the return ip on the stack.
"movq %rdx, 104(%rsp, %rsi, 8);"
"testq %rsi, %rsi;"
"jne _RecordReplayRedirectCall_Loop;"
// Place the CallEvent being made into the first argument register.
"movq %rax, %rdi;"
// Place the structure's address into the second argument register.
"movq %rsp, %rsi;"
// Determine how to handle this call.
"call _RecordReplayInterceptCall;"
"testq %rax, %rax;"
"je RecordReplayRedirectCall_done;"
// Events are passed through. The function to call has been stored in rval0.
// Call the function with the original stack and register state.
"movq 0(%rsp), %rdi;"
"movq 8(%rsp), %rsi;"
"movq 16(%rsp), %rdx;"
"movq 24(%rsp), %rcx;"
"movq 32(%rsp), %r8;"
"movq 40(%rsp), %r9;"
"movsd 48(%rsp), %xmm0;"
"movsd 56(%rsp), %xmm1;"
"movsd 64(%rsp), %xmm2;"
"movq 72(%rsp), %rax;"
"addq $616, %rsp;"
"jmpq *%rax;"
// The message has been recorded/replayed.
"RecordReplayRedirectCall_done:"
// Restore scalar and floating point return values.
"movq 72(%rsp), %rax;"
"movq 80(%rsp), %rdx;"
"movsd 88(%rsp), %xmm0;"
"movsd 96(%rsp), %xmm1;"
// Pop the structure from the stack.
"addq $616, %rsp;"
// Return to caller.
"ret;"
);
// Call a function address with the specified arguments.
extern void
RecordReplayInvokeCallRaw(CallArguments* aArguments, void* aFnPtr);
__asm(
"_RecordReplayInvokeCallRaw:"
// Save function pointer in rax.
"movq %rsi, %rax;"
// Save arguments on the stack. This also aligns the stack.
"push %rdi;"
// Count how many stack arguments we need to copy.
"movq $64, %rsi;"
// Enter the loop below. The compiler might not place this block of code
// adjacent to the loop, so perform the jump explicitly.
"jmp _RecordReplayInvokeCallRaw_Loop;"
// Copy each stack argument to the stack.
"_RecordReplayInvokeCallRaw_Loop:"
"subq $1, %rsi;"
"movq 104(%rdi, %rsi, 8), %rdx;"
"push %rdx;"
"testq %rsi, %rsi;"
"jne _RecordReplayInvokeCallRaw_Loop;"
// Copy each register argument into the appropriate register.
"movq 8(%rdi), %rsi;"
"movq 16(%rdi), %rdx;"
"movq 24(%rdi), %rcx;"
"movq 32(%rdi), %r8;"
"movq 40(%rdi), %r9;"
"movsd 48(%rdi), %xmm0;"
"movsd 56(%rdi), %xmm1;"
"movsd 64(%rdi), %xmm2;"
"movq 0(%rdi), %rdi;"
// Call the saved function pointer.
"callq *%rax;"
// Pop the copied stack arguments.
"addq $512, %rsp;"
// Save any return values to the arguments.
"pop %rdi;"
"movq %rax, 72(%rdi);"
"movq %rdx, 80(%rdi);"
"movsd %xmm0, 88(%rdi);"
"movsd %xmm1, 96(%rdi);"
"ret;"
);
} // extern "C"
MOZ_NEVER_INLINE void
RecordReplayInvokeCall(size_t aCallId, CallArguments* aArguments)
{
RecordReplayInvokeCallRaw(aArguments, OriginalFunction(aCallId));
}
///////////////////////////////////////////////////////////////////////////////
// Library API Redirections
///////////////////////////////////////////////////////////////////////////////
@ -413,12 +625,22 @@ FunctionStartAddress(Redirection& aRedirection)
return addr;
}
// Generate code to set %rax and enter RecordReplayRedirectCall.
static uint8_t*
GenerateRedirectStub(Assembler& aAssembler, size_t aCallId)
{
uint8_t* newFunction = aAssembler.Current();
aAssembler.MoveImmediateToRax((void*) aCallId);
aAssembler.Jump(BitwiseCast<void*>(RecordReplayRedirectCall));
return newFunction;
}
// Setup a redirection: overwrite the machine code for its base function, and
// fill in its original function, to satisfy the function pointer behaviors
// described in the Redirection structure. aCursor and aCursorEnd are used to
// allocate executable memory for use in the redirection.
static void
Redirect(Redirection& aRedirection, Assembler& aAssembler, bool aFirstPass)
Redirect(size_t aCallId, Redirection& aRedirection, Assembler& aAssembler, bool aFirstPass)
{
// The patching we do here might fail: it isn't possible to redirect an
// arbitrary instruction pointer within an arbitrary block of code. This code
@ -475,7 +697,8 @@ Redirect(Redirection& aRedirection, Assembler& aAssembler, bool aFirstPass)
aAssembler.Jump(ro);
// Emit jump J0.
AddJumpPatch(functionStart, aRedirection.mNewFunction, /* aShort = */ false);
uint8_t* newFunction = GenerateRedirectStub(aAssembler, aCallId);
AddJumpPatch(functionStart, newFunction, /* aShort = */ false);
AddClobberPatch(functionStart + JumpBytesClobberRax, ro);
return;
}
@ -542,7 +765,8 @@ Redirect(Redirection& aRedirection, Assembler& aAssembler, bool aFirstPass)
AddJumpPatch(ro, firstJumpTarget, /* aShort = */ false);
// Emit jump J2.
AddJumpPatch(ro + JumpBytesClobberRax, aRedirection.mNewFunction, /* aShort = */ false);
uint8_t* newFunction = GenerateRedirectStub(aAssembler, aCallId);
AddJumpPatch(ro + JumpBytesClobberRax, newFunction, /* aShort = */ false);
AddClobberPatch(ro + 2 * JumpBytesClobberRax, afterip);
// Emit jump J0.
@ -559,7 +783,6 @@ EarlyInitializeRedirections()
break;
}
MOZ_ASSERT(!redirection.mBaseFunction);
MOZ_ASSERT(redirection.mNewFunction);
MOZ_ASSERT(!redirection.mOriginalFunction);
redirection.mBaseFunction = FunctionStartAddress(redirection);
@ -592,7 +815,7 @@ InitializeRedirections()
if (!redirection.mName) {
break;
}
Redirect(redirection, assembler, /* aFirstPass = */ true);
Redirect(i, redirection, assembler, /* aFirstPass = */ true);
}
for (size_t i = 0;; i++) {
@ -600,7 +823,7 @@ InitializeRedirections()
if (!redirection.mName) {
break;
}
Redirect(redirection, assembler, /* aFirstPass = */ false);
Redirect(i, redirection, assembler, /* aFirstPass = */ false);
}
}

Просмотреть файл

@ -49,9 +49,10 @@ namespace recordreplay {
// function is *not* called, but rather the event and outputs are read from
// the recording and sent back to the caller.
//
// Macros are provided below to streamline this process. Redirections do not
// need to adhere to this protocol, however, and can have whatever behaviors
// that are necessary for reliable record/replay.
// Redirections do not need to adhere to this protocol, however, and can have
// whatever behaviors that are necessary for reliable record/replay.
// Controlling how a redirected function behaves and which outputs it saves is
// done through a set of hooks associated with each redirection.
//
// Some platforms need additional redirection techniques for handling different
// features of that platform. See the individual ProcessRedirect*.cpp files for
@ -73,6 +74,96 @@ namespace recordreplay {
// Function Redirections
///////////////////////////////////////////////////////////////////////////////
// Capture the arguments that can be passed to a redirection, and provide
// storage to specify the redirection's return value. We only need to capture
// enough argument data here for calls made directly from Gecko code,
// i.e. where events are not passed through. Calls made while events are passed
// through are performed with the same stack and register state as when they
// were initially invoked.
//
// Arguments and return value indexes refer to the register contents as passed
// to the function originally. For functions with complex or floating point
// arguments and return values, the right index to use might be different than
// expected, per the requirements of the System V x64 ABI.
struct CallArguments
{
protected:
size_t arg0; // 0
size_t arg1; // 8
size_t arg2; // 16
size_t arg3; // 24
size_t arg4; // 32
size_t arg5; // 40
double floatarg0; // 48
double floatarg1; // 56
double floatarg2; // 64
size_t rval0; // 72
size_t rval1; // 80
double floatrval0; // 88
double floatrval1; // 96
size_t stack[64]; // 104
// Size: 616
public:
template <size_t Index, typename T>
T& Arg() {
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) {
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];
}
}
template <typename T, size_t Index = 0>
T& Rval() {
static_assert(sizeof(T) == sizeof(size_t), "Size must match");
static_assert(IsFloatingPoint<T>::value == false, "Use FloatRval instead");
static_assert(Index == 0 || Index == 1, "Bad index");
switch (Index) {
case 0: return (T&)rval0;
case 1: return (T&)rval1;
}
}
template <size_t Index = 0>
double& FloatRval() {
static_assert(Index == 0 || Index == 1, "Bad index");
switch (Index) {
case 0: return floatrval0;
case 1: return floatrval1;
}
}
};
// Generic type for a system error code.
typedef ssize_t ErrorType;
// Signature for a function that saves some output produced by a redirection
// while recording, and restores that output while replaying. aEvents is the
// event stream for the current thread, aArguments specifies the arguments to
// the called function, and aError specifies any system error which the call
// produces.
typedef void (*SaveOutputFn)(Stream& aEvents, CallArguments* aArguments, ErrorType* aError);
// Possible results for the redirection preamble hook.
enum class PreambleResult {
// Do not perform any further processing.
Veto,
// Perform a function redirection as normal if events are not passed through.
Redirect,
// Do not add an event for the call, as if events were passed through.
PassThrough
};
// Information about a system library API function which is being redirected.
struct Redirection
{
@ -81,16 +172,22 @@ struct Redirection
// Address of the function which is being redirected. The code for this
// function is modified so that attempts to call this function will instead
// call mNewFunction.
// call into RecordReplayInterceptCall.
uint8_t* mBaseFunction;
// Function with the same signature as mBaseFunction, which may have
// different behavior for recording/replaying the call.
uint8_t* mNewFunction;
// Function with the same signature and original behavior as
// mBaseFunction.
uint8_t* mOriginalFunction;
// Redirection hooks are used to control the behavior of the redirected
// function.
// If specified, will be called at the end of the redirection when events are
// not being passed through to record/replay any outputs from the call.
SaveOutputFn mSaveOutput;
// If specified, will be called upon entry to the redirected call.
PreambleResult (*mPreamble)(CallArguments* aArguments);
};
// All platform specific redirections, indexed by the call event.
@ -105,9 +202,6 @@ void EarlyInitializeRedirections();
// gInitializationFailureMessage.
bool InitializeRedirections();
// Generic type for a system error code.
typedef ssize_t ErrorType;
// Functions for saving or restoring system error codes.
static inline ErrorType SaveError() { return errno; }
static inline void RestoreError(ErrorType aError) { errno = aError; }
@ -143,510 +237,8 @@ CallIdToThreadEvent(size_t aCallId)
return (ThreadEvent)((uint32_t)ThreadEvent::CallStart + aCallId);
}
// State for a function redirection which performs the standard steps (see the
// comment at the start of this file). This should not be created directly, but
// rather through one of the macros below.
struct AutoRecordReplayFunctionVoid
{
// The current thread, or null if events are being passed through.
Thread* mThread;
// Any system error generated by the call which was redirected.
ErrorType mError;
protected:
// Information about the call being recorded.
size_t mCallId;
public:
explicit AutoRecordReplayFunctionVoid(size_t aCallId)
: mThread(Thread::Current()), mError(0), mCallId(aCallId)
{
if (mThread && mThread->PassThroughEvents()) {
mThread = nullptr;
} else if (mThread) {
// Calling any redirection which performs the standard steps will cause
// debugger operations that have diverged from the recording to fail.
EnsureNotDivergedFromRecording();
MOZ_RELEASE_ASSERT(mThread->CanAccessRecording());
// Pass through events in case we are calling the original function.
mThread->SetPassThrough(true);
}
}
~AutoRecordReplayFunctionVoid()
{
if (mThread) {
// Restore any error saved or replayed earlier to the system.
RestoreError(mError);
}
}
// Begin recording or replaying data for the call. This must be called before
// destruction if mThread is non-null.
inline void StartRecordReplay() {
MOZ_ASSERT(mThread);
// Save any system error in case we want to record/replay it.
mError = SaveError();
// Stop the event passing through that was initiated in the constructor.
mThread->SetPassThrough(false);
// Add an event for the thread.
mThread->Events().RecordOrReplayThreadEvent(CallIdToThreadEvent(mCallId));
}
};
// State for a function redirection that performs the standard steps and also
// returns a value.
template <typename ReturnType>
struct AutoRecordReplayFunction : AutoRecordReplayFunctionVoid
{
// The value which this function call should return.
ReturnType mRval;
explicit AutoRecordReplayFunction(size_t aCallId)
: AutoRecordReplayFunctionVoid(aCallId)
{}
};
// Macros for recording or replaying a function that performs the standard
// steps. These macros should be used near the start of the body of a
// redirection function, and will fall through only if events are not
// passed through and the outputs of the function need to be recorded or
// replayed.
//
// These macros define an AutoRecordReplayFunction local |rrf| with state for
// the redirection, and additional locals |events| and (if the function has a
// return value) |rval| for convenient access.
// Record/replay a function that returns a value and has a particular ABI.
#define RecordReplayFunctionABI(aName, aReturnType, aABI, ...) \
AutoRecordReplayFunction<aReturnType> rrf(CallEvent_ ##aName); \
if (!rrf.mThread) { \
return OriginalCallABI(aName, aReturnType, aABI, ##__VA_ARGS__); \
} \
if (IsRecording()) { \
rrf.mRval = OriginalCallABI(aName, aReturnType, aABI, ##__VA_ARGS__); \
} \
rrf.StartRecordReplay(); \
Stream& events = rrf.mThread->Events(); \
(void) events; \
aReturnType& rval = rrf.mRval
// Record/replay a function that returns a value and has the default ABI.
#define RecordReplayFunction(aName, aReturnType, ...) \
RecordReplayFunctionABI(aName, aReturnType, DEFAULTABI, ##__VA_ARGS__)
// Record/replay a function that has no return value and has a particular ABI.
#define RecordReplayFunctionVoidABI(aName, aABI, ...) \
AutoRecordReplayFunctionVoid rrf(CallEvent_ ##aName); \
if (!rrf.mThread) { \
OriginalCallABI(aName, void, aABI, ##__VA_ARGS__); \
return; \
} \
if (IsRecording()) { \
OriginalCallABI(aName, void, aABI, ##__VA_ARGS__); \
} \
rrf.StartRecordReplay(); \
Stream& events = rrf.mThread->Events(); \
(void) events
// Record/replay a function that has no return value and has the default ABI.
#define RecordReplayFunctionVoid(aName, ...) \
RecordReplayFunctionVoidABI(aName, DEFAULTABI, ##__VA_ARGS__)
// The following macros are used for functions that do not record an error and
// take or return values of specified types.
//
// aAT == aArgumentType
// aRT == aReturnType
#define RRFunctionTypes0(aName, aRT) \
static aRT DEFAULTABI \
RR_ ##aName () \
{ \
RecordReplayFunction(aName, aRT); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes1(aName, aRT, aAT0) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0) \
{ \
RecordReplayFunction(aName, aRT, a0); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes2(aName, aRT, aAT0, aAT1) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1) \
{ \
RecordReplayFunction(aName, aRT, a0, a1); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes3(aName, aRT, aAT0, aAT1, aAT2) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes4(aName, aRT, aAT0, aAT1, aAT2, aAT3) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2, a3); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes5(aName, aRT, aAT0, aAT1, aAT2, aAT3, \
aAT4) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3, aAT4 a4) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2, a3, a4); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes6(aName, aRT, aAT0, aAT1, aAT2, aAT3, \
aAT4, aAT5) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3, aAT4 a4, \
aAT5 a5) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2, a3, a4, a5); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes7(aName, aRT, aAT0, aAT1, aAT2, aAT3, \
aAT4, aAT5, aAT6) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3, aAT4 a4, \
aAT5 a5, aAT6 a6) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2, a3, a4, a5, a6); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes8(aName, aRT, aAT0, aAT1, aAT2, aAT3, \
aAT4, aAT5, aAT6, aAT7) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3, aAT4 a4, \
aAT5 a5, aAT6 a6, aAT7 a7) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2, a3, a4, a5, a6, a7); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes9(aName, aRT, aAT0, aAT1, aAT2, aAT3, \
aAT4, aAT5, aAT6, aAT7, aAT8) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3, aAT4 a4, \
aAT5 a5, aAT6 a6, aAT7 a7, aAT8 a8) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2, a3, a4, a5, a6, a7, a8); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypes10(aName, aRT, aAT0, aAT1, aAT2, aAT3, \
aAT4, aAT5, aAT6, aAT7, aAT8, aAT9) \
static aRT DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3, aAT4 a4, \
aAT5 a5, aAT6 a6, aAT7 a7, aAT8 a8, aAT9 a9) \
{ \
RecordReplayFunction(aName, aRT, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); \
events.RecordOrReplayValue(&rval); \
return rval; \
}
#define RRFunctionTypesVoid1(aName, aAT0) \
static void DEFAULTABI \
RR_ ##aName (aAT0 a0) \
{ \
RecordReplayFunctionVoid(aName, a0); \
}
#define RRFunctionTypesVoid2(aName, aAT0, aAT1) \
static void DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1) \
{ \
RecordReplayFunctionVoid(aName, a0, a1); \
}
#define RRFunctionTypesVoid3(aName, aAT0, aAT1, aAT2) \
static void DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2) \
{ \
RecordReplayFunctionVoid(aName, a0, a1, a2); \
}
#define RRFunctionTypesVoid4(aName, aAT0, aAT1, aAT2, aAT3) \
static void DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3) \
{ \
RecordReplayFunctionVoid(aName, a0, a1, a2, a3); \
}
#define RRFunctionTypesVoid5(aName, aAT0, aAT1, aAT2, aAT3, aAT4) \
static void DEFAULTABI \
RR_ ##aName (aAT0 a0, aAT1 a1, aAT2 a2, aAT3 a3, aAT4 a4) \
{ \
RecordReplayFunctionVoid(aName, a0, a1, a2, a3, a4); \
}
// The following macros are used for functions that take and return scalar
// values (not a struct or a floating point) and do not record an error
// anywhere.
#define RRFunction0(aName) \
RRFunctionTypes0(aName, size_t)
#define RRFunction1(aName) \
RRFunctionTypes1(aName, size_t, size_t)
#define RRFunction2(aName) \
RRFunctionTypes2(aName, size_t, size_t, size_t)
#define RRFunction3(aName) \
RRFunctionTypes3(aName, size_t, size_t, size_t, size_t)
#define RRFunction4(aName) \
RRFunctionTypes4(aName, size_t, size_t, size_t, size_t, size_t)
#define RRFunction5(aName) \
RRFunctionTypes5(aName, size_t, size_t, size_t, size_t, size_t, size_t)
#define RRFunction6(aName) \
RRFunctionTypes6(aName, size_t, size_t, size_t, size_t, size_t, size_t, size_t)
#define RRFunction7(aName) \
RRFunctionTypes7(aName, size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t)
#define RRFunction8(aName) \
RRFunctionTypes8(aName, size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t, \
size_t)
#define RRFunction9(aName) \
RRFunctionTypes9(aName, size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t, \
size_t, size_t)
#define RRFunction10(aName) \
RRFunctionTypes10(aName, size_t, size_t, size_t, size_t, size_t, size_t, size_t, size_t, \
size_t, size_t, size_t)
// The following macros are used for functions that take scalar arguments and
// do not return a value or record an error anywhere.
#define RRFunctionVoid0(aName) \
static void DEFAULTABI \
RR_ ##aName () \
{ \
RecordReplayFunctionVoid(aName); \
}
#define RRFunctionVoid1(aName) \
RRFunctionTypesVoid1(aName, size_t)
#define RRFunctionVoid2(aName) \
RRFunctionTypesVoid2(aName, size_t, size_t)
#define RRFunctionVoid3(aName) \
RRFunctionTypesVoid3(aName, size_t, size_t, size_t)
#define RRFunctionVoid4(aName) \
RRFunctionTypesVoid4(aName, size_t, size_t, size_t, size_t)
#define RRFunctionVoid5(aName) \
RRFunctionTypesVoid5(aName, size_t, size_t, size_t, size_t, size_t)
// The following macros are used for functions that return a signed integer
// value and record an error if the return value is negative.
#define RRFunctionNegError0(aName) \
static ssize_t DEFAULTABI \
RR_ ##aName () \
{ \
RecordReplayFunction(aName, ssize_t); \
RecordOrReplayHadErrorNegative(rrf); \
return rval; \
}
#define RRFunctionNegError1(aName) \
static ssize_t DEFAULTABI \
RR_ ##aName (size_t a0) \
{ \
RecordReplayFunction(aName, ssize_t, a0); \
RecordOrReplayHadErrorNegative(rrf); \
return rval; \
}
#define RRFunctionNegError2(aName) \
static ssize_t DEFAULTABI \
RR_ ##aName (size_t a0, size_t a1) \
{ \
RecordReplayFunction(aName, ssize_t, a0, a1); \
RecordOrReplayHadErrorNegative(rrf); \
return rval; \
}
#define RRFunctionNegError3(aName) \
static ssize_t DEFAULTABI \
RR_ ##aName (size_t a0, size_t a1, size_t a2) \
{ \
RecordReplayFunction(aName, ssize_t, a0, a1, a2); \
RecordOrReplayHadErrorNegative(rrf); \
return rval; \
}
#define RRFunctionNegError4(aName) \
static ssize_t DEFAULTABI \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3) \
{ \
RecordReplayFunction(aName, ssize_t, a0, a1, a2, a3); \
RecordOrReplayHadErrorNegative(rrf); \
return rval; \
}
#define RRFunctionNegError5(aName) \
static ssize_t DEFAULTABI \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3, \
size_t a4) \
{ \
RecordReplayFunction(aName, ssize_t, a0, a1, a2, a3, a4); \
RecordOrReplayHadErrorNegative(rrf); \
return rval; \
}
#define RRFunctionNegError6(aName) \
static ssize_t DEFAULTABI \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3, \
size_t a4, size_t a5) \
{ \
RecordReplayFunction(aName, ssize_t, a0, a1, a2, a3, a4, a5); \
RecordOrReplayHadErrorNegative(rrf); \
return rval; \
}
// The following macros are used for functions that return an integer
// value and record an error if the return value is zero.
#define RRFunctionZeroError0(aName) \
static size_t __stdcall \
RR_ ##aName () \
{ \
RecordReplayFunction(aName, size_t); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroError1(aName) \
static size_t __stdcall \
RR_ ##aName (size_t a0) \
{ \
RecordReplayFunction(aName, size_t, a0); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroErrorABI2(aName, aABI) \
static size_t aABI \
RR_ ##aName (size_t a0, size_t a1) \
{ \
RecordReplayFunctionABI(aName, size_t, aABI, a0, a1); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroError2(aName) RRFunctionZeroErrorABI2(aName, DEFAULTABI)
#define RRFunctionZeroError3(aName) \
static size_t __stdcall \
RR_ ##aName (size_t a0, size_t a1, size_t a2) \
{ \
RecordReplayFunction(aName, size_t, a0, a1, a2); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroError4(aName) \
static size_t __stdcall \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3) \
{ \
RecordReplayFunction(aName, size_t, a0, a1, a2, a3); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroError5(aName) \
static size_t __stdcall \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3, \
size_t a4) \
{ \
RecordReplayFunction(aName, size_t, a0, a1, a2, a3, a4); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroError6(aName) \
static size_t __stdcall \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3, \
size_t a4, size_t a5) \
{ \
RecordReplayFunction(aName, size_t, a0, a1, a2, a3, a4, a5); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroError7(aName) \
static size_t __stdcall \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3, \
size_t a4, size_t a5, size_t a6) \
{ \
RecordReplayFunction(aName, size_t, a0, a1, a2, a3, a4, a5, a6); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
#define RRFunctionZeroError8(aName) \
static size_t __stdcall \
RR_ ##aName (size_t a0, size_t a1, size_t a2, size_t a3, \
size_t a4, size_t a5, size_t a6, size_t a7) \
{ \
RecordReplayFunction(aName, size_t, a0, a1, a2, a3, a4, a5, a6, a7); \
RecordOrReplayHadErrorZero(rrf); \
return rval; \
}
// Recording template for functions which are used for inter-thread
// synchronization and must be replayed in the original order they executed in.
#define RecordReplayOrderedFunction(aName, aReturnType, aFailureRval, aFormals, ...) \
static aReturnType DEFAULTABI \
RR_ ## aName aFormals \
{ \
BeginOrderedEvent(); /* This is a noop if !mThread */ \
RecordReplayFunction(aName, aReturnType, __VA_ARGS__); \
EndOrderedEvent(); \
events.RecordOrReplayValue(&rval); \
if (rval == aFailureRval) { \
events.RecordOrReplayValue(&rrf.mError); \
} \
return rval; \
}
void
RecordReplayInvokeCall(size_t aCallId, CallArguments* aArguments);
///////////////////////////////////////////////////////////////////////////////
// Callback Redirections
@ -711,32 +303,6 @@ struct AutoRecordReplayCallback
// Redirection Helpers
///////////////////////////////////////////////////////////////////////////////
// Read/write a success code (where zero is failure) and errno value on failure.
template <typename T>
static inline bool
RecordOrReplayHadErrorZero(AutoRecordReplayFunction<T>& aRrf)
{
aRrf.mThread->Events().RecordOrReplayValue(&aRrf.mRval);
if (aRrf.mRval == 0) {
aRrf.mThread->Events().RecordOrReplayValue(&aRrf.mError);
return true;
}
return false;
}
// Read/write a success code (where negative values are failure) and errno value on failure.
template <typename T>
static inline bool
RecordOrReplayHadErrorNegative(AutoRecordReplayFunction<T>& aRrf)
{
aRrf.mThread->Events().RecordOrReplayValue(&aRrf.mRval);
if (aRrf.mRval < 0) {
aRrf.mThread->Events().RecordOrReplayValue(&aRrf.mError);
return true;
}
return false;
}
extern Atomic<size_t, SequentiallyConsistent, Behavior::DontPreserve> gMemoryLeakBytes;
// For allocating memory in redirections that will never be reclaimed. This is
@ -751,6 +317,228 @@ NewLeakyArray(size_t aSize)
return new T[aSize];
}
// Record/replay a string rval.
static inline void
RR_CStringRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& rval = aArguments->Rval<char*>();
size_t len = (IsRecording() && rval) ? strlen(rval) + 1 : 0;
aEvents.RecordOrReplayValue(&len);
if (IsReplaying()) {
rval = len ? NewLeakyArray<char>(len) : nullptr;
}
if (len) {
aEvents.RecordOrReplayBytes(rval, len);
}
}
// Ensure that the return value matches the specified argument.
template <size_t Argument>
static inline void
RR_RvalIsArgument(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& rval = aArguments->Rval<size_t>();
auto& arg = aArguments->Arg<Argument, size_t>();
if (IsRecording()) {
MOZ_RELEASE_ASSERT(rval == arg);
} else {
rval = arg;
}
}
// No-op record/replay output method.
static inline void
RR_NoOp(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
}
// Record/replay multiple SaveOutput functions sequentially.
template <SaveOutputFn Fn0,
SaveOutputFn Fn1,
SaveOutputFn Fn2 = RR_NoOp,
SaveOutputFn Fn3 = RR_NoOp,
SaveOutputFn Fn4 = RR_NoOp>
static inline void
RR_Compose(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
Fn0(aEvents, aArguments, aError);
Fn1(aEvents, aArguments, aError);
Fn2(aEvents, aArguments, aError);
Fn3(aEvents, aArguments, aError);
Fn4(aEvents, aArguments, aError);
}
// Record/replay a success code rval (where negative values are failure) and
// errno value on failure. SuccessFn does any further processing in non-error
// cases.
template <SaveOutputFn SuccessFn = RR_NoOp>
static inline void
RR_SaveRvalHadErrorNegative(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& rval = aArguments->Rval<ssize_t>();
aEvents.RecordOrReplayValue(&rval);
if (rval < 0) {
aEvents.RecordOrReplayValue(aError);
} else {
SuccessFn(aEvents, aArguments, aError);
}
}
// Record/replay a success code rval (where zero is a failure) and errno value
// on failure. SuccessFn does any further processing in non-error cases.
template <SaveOutputFn SuccessFn = RR_NoOp>
static inline void
RR_SaveRvalHadErrorZero(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& rval = aArguments->Rval<ssize_t>();
aEvents.RecordOrReplayValue(&rval);
if (rval == 0) {
aEvents.RecordOrReplayValue(aError);
} else {
SuccessFn(aEvents, aArguments, aError);
}
}
// Record/replay the contents of a buffer at argument BufferArg with element
// count at CountArg.
template <size_t BufferArg, size_t CountArg, typename ElemType = char>
static inline void
RR_WriteBuffer(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& buf = aArguments->Arg<BufferArg, ElemType*>();
auto& count = aArguments->Arg<CountArg, size_t>();
aEvents.CheckInput(count);
aEvents.RecordOrReplayBytes(buf, count * sizeof(ElemType));
}
// Record/replay the contents of a buffer at argument BufferArg with element
// count at CountArg, and which may be null.
template <size_t BufferArg, size_t CountArg, typename ElemType = char>
static inline void
RR_WriteOptionalBuffer(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& buf = aArguments->Arg<BufferArg, ElemType*>();
auto& count = aArguments->Arg<CountArg, size_t>();
aEvents.CheckInput(!!buf);
if (buf) {
aEvents.CheckInput(count);
aEvents.RecordOrReplayBytes(buf, count * sizeof(ElemType));
}
}
// Record/replay the contents of a buffer at argument BufferArg with fixed
// size ByteCount.
template <size_t BufferArg, size_t ByteCount>
static inline void
RR_WriteBufferFixedSize(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& buf = aArguments->Arg<BufferArg, void*>();
aEvents.RecordOrReplayBytes(buf, ByteCount);
}
// Record/replay the contents of a buffer at argument BufferArg with fixed
// size ByteCount, and which may be null.
template <size_t BufferArg, size_t ByteCount>
static inline void
RR_WriteOptionalBufferFixedSize(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& buf = aArguments->Arg<BufferArg, void*>();
aEvents.CheckInput(!!buf);
if (buf) {
aEvents.RecordOrReplayBytes(buf, ByteCount);
}
}
// Record/replay the contents of a buffer at argument BufferArg with byte size
// CountArg, where the call return value plus Offset indicates the amount of
// data written to the buffer by the call. The return value must already have
// been recorded/replayed.
template <size_t BufferArg, size_t CountArg, size_t Offset = 0>
static inline void
RR_WriteBufferViaRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
auto& buf = aArguments->Arg<BufferArg, void*>();
auto& count = aArguments->Arg<CountArg, size_t>();
aEvents.CheckInput(count);
auto& rval = aArguments->Rval<size_t>();
MOZ_RELEASE_ASSERT(rval + Offset <= count);
aEvents.RecordOrReplayBytes(buf, rval + Offset);
}
// Insert an atomic access while recording/replaying so that calls to this
// function replay in the same order they occurred in while recording. This is
// used for functions that are used in inter-thread synchronization.
static inline void
RR_OrderCall(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
AutoOrderedAtomicAccess();
}
// Record/replay a scalar return value.
static inline void
RR_ScalarRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
aEvents.RecordOrReplayValue(&aArguments->Rval<size_t>());
}
// Record/replay a complex scalar return value that fits in two registers.
static inline void
RR_ComplexScalarRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
aEvents.RecordOrReplayValue(&aArguments->Rval<size_t>());
aEvents.RecordOrReplayValue(&aArguments->Rval<size_t, 1>());
}
// Record/replay a floating point return value.
static inline void
RR_FloatRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
aEvents.RecordOrReplayValue(&aArguments->FloatRval());
}
// Record/replay a complex floating point return value that fits in two registers.
static inline void
RR_ComplexFloatRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
aEvents.RecordOrReplayValue(&aArguments->FloatRval());
aEvents.RecordOrReplayValue(&aArguments->FloatRval<1>());
}
// Record/replay a return value that does not fit in the return registers,
// and whose storage is pointed to by the first argument register.
template <size_t ByteCount>
static inline void
RR_OversizeRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
{
RR_WriteBufferFixedSize<0, ByteCount>(aEvents, aArguments, aError);
}
template <size_t ReturnValue>
static inline PreambleResult
Preamble_Veto(CallArguments* aArguments)
{
aArguments->Rval<size_t>() = 0;
return PreambleResult::Veto;
}
template <size_t ReturnValue>
static inline PreambleResult
Preamble_VetoIfNotPassedThrough(CallArguments* aArguments)
{
if (AreThreadEventsPassedThrough()) {
return PreambleResult::PassThrough;
}
aArguments->Rval<size_t>() = 0;
return PreambleResult::Veto;
}
static inline PreambleResult
Preamble_PassThrough(CallArguments* aArguments)
{
return PreambleResult::PassThrough;
}
///////////////////////////////////////////////////////////////////////////////
// Other Redirection Interfaces
///////////////////////////////////////////////////////////////////////////////

Разница между файлами не показана из-за своего большого размера Загрузить разницу