diff --git a/toolkit/recordreplay/ProcessRedirect.cpp b/toolkit/recordreplay/ProcessRedirect.cpp index 3f45dd6a7d47..0eb38b0aa3f3 100644 --- a/toolkit/recordreplay/ProcessRedirect.cpp +++ b/toolkit/recordreplay/ProcessRedirect.cpp @@ -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() = 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(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); } } diff --git a/toolkit/recordreplay/ProcessRedirect.h b/toolkit/recordreplay/ProcessRedirect.h index eeb097241311..e831ef7b08c9 100644 --- a/toolkit/recordreplay/ProcessRedirect.h +++ b/toolkit/recordreplay/ProcessRedirect.h @@ -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 + T& Arg() { + static_assert(sizeof(T) == sizeof(size_t), "Size must match"); + static_assert(IsFloatingPoint::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 + T& Rval() { + static_assert(sizeof(T) == sizeof(size_t), "Size must match"); + static_assert(IsFloatingPoint::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 + 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 -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 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 -static inline bool -RecordOrReplayHadErrorZero(AutoRecordReplayFunction& 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 -static inline bool -RecordOrReplayHadErrorNegative(AutoRecordReplayFunction& aRrf) -{ - aRrf.mThread->Events().RecordOrReplayValue(&aRrf.mRval); - if (aRrf.mRval < 0) { - aRrf.mThread->Events().RecordOrReplayValue(&aRrf.mError); - return true; - } - return false; -} - extern Atomic 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(); + size_t len = (IsRecording() && rval) ? strlen(rval) + 1 : 0; + aEvents.RecordOrReplayValue(&len); + if (IsReplaying()) { + rval = len ? NewLeakyArray(len) : nullptr; + } + if (len) { + aEvents.RecordOrReplayBytes(rval, len); + } +} + +// Ensure that the return value matches the specified argument. +template +static inline void +RR_RvalIsArgument(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& rval = aArguments->Rval(); + auto& arg = aArguments->Arg(); + 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 +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 +static inline void +RR_SaveRvalHadErrorNegative(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& rval = aArguments->Rval(); + 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 +static inline void +RR_SaveRvalHadErrorZero(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& rval = aArguments->Rval(); + 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 +static inline void +RR_WriteBuffer(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& buf = aArguments->Arg(); + auto& count = aArguments->Arg(); + 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 +static inline void +RR_WriteOptionalBuffer(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& buf = aArguments->Arg(); + auto& count = aArguments->Arg(); + 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 +static inline void +RR_WriteBufferFixedSize(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& buf = aArguments->Arg(); + aEvents.RecordOrReplayBytes(buf, ByteCount); +} + +// Record/replay the contents of a buffer at argument BufferArg with fixed +// size ByteCount, and which may be null. +template +static inline void +RR_WriteOptionalBufferFixedSize(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& buf = aArguments->Arg(); + 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 +static inline void +RR_WriteBufferViaRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + auto& buf = aArguments->Arg(); + auto& count = aArguments->Arg(); + aEvents.CheckInput(count); + + auto& rval = aArguments->Rval(); + 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()); +} + +// 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()); + aEvents.RecordOrReplayValue(&aArguments->Rval()); +} + +// 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 +static inline void +RR_OversizeRval(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) +{ + RR_WriteBufferFixedSize<0, ByteCount>(aEvents, aArguments, aError); +} + +template +static inline PreambleResult +Preamble_Veto(CallArguments* aArguments) +{ + aArguments->Rval() = 0; + return PreambleResult::Veto; +} + +template +static inline PreambleResult +Preamble_VetoIfNotPassedThrough(CallArguments* aArguments) +{ + if (AreThreadEventsPassedThrough()) { + return PreambleResult::PassThrough; + } + aArguments->Rval() = 0; + return PreambleResult::Veto; +} + +static inline PreambleResult +Preamble_PassThrough(CallArguments* aArguments) +{ + return PreambleResult::PassThrough; +} + /////////////////////////////////////////////////////////////////////////////// // Other Redirection Interfaces /////////////////////////////////////////////////////////////////////////////// diff --git a/toolkit/recordreplay/ProcessRedirectDarwin.cpp b/toolkit/recordreplay/ProcessRedirectDarwin.cpp index 09650fc9f9ce..d210d8ab3863 100644 --- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp +++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp @@ -41,377 +41,451 @@ namespace mozilla { namespace recordreplay { -#define FOR_EACH_REDIRECTION(MACRO) \ - /* System call wrappers */ \ - MACRO(kevent) \ - MACRO(kevent64) \ - MACRO(madvise) \ - MACRO(mprotect) \ - MACRO(mmap) \ - MACRO(munmap) \ - MACRO(read) \ - MACRO(__read_nocancel) \ - MACRO(pread) \ - MACRO(write) \ - MACRO(__write_nocancel) \ - MACRO(open) \ - MACRO(__open_nocancel) \ - MACRO(recv) \ - MACRO(recvmsg) \ - MACRO(sendmsg) \ - MACRO(shm_open) \ - MACRO(socket) \ - MACRO(kqueue) \ - MACRO(pipe) \ - MACRO(close) \ - MACRO(__close_nocancel) \ - MACRO(mkdir) \ - MACRO(dup) \ - MACRO(access) \ - MACRO(lseek) \ - MACRO(socketpair) \ - MACRO(fileport_makeport) \ - MACRO(getsockopt) \ - MACRO(gettimeofday) \ - MACRO(getuid) \ - MACRO(geteuid) \ - MACRO(getgid) \ - MACRO(getegid) \ - MACRO(issetugid) \ - MACRO(__gettid) \ - MACRO(getpid) \ - MACRO(fcntl) \ - MACRO(fstat$INODE64) \ - MACRO(lstat$INODE64) \ - MACRO(stat$INODE64) \ - MACRO(getattrlist) \ - MACRO(statfs$INODE64) \ - MACRO(fstatfs$INODE64) \ - MACRO(readlink) \ - MACRO(__getdirentries64) \ - MACRO(getdirentriesattr) \ - MACRO(getrusage) \ - MACRO(__getrlimit) \ - MACRO(__setrlimit) \ - MACRO(sigprocmask) \ - MACRO(sigaltstack) \ - MACRO(sigaction) \ - MACRO(__pthread_sigmask) \ - MACRO(__fsgetpath) \ - MACRO(__disable_threadsignal) \ - MACRO(__sysctl) \ - MACRO(__mac_syscall) \ - MACRO(getaudit_addr) \ - MACRO(umask) \ - MACRO(__select) \ - MACRO(__process_policy) \ - MACRO(__kdebug_trace) \ - MACRO(guarded_kqueue_np) \ - MACRO(csops) \ - MACRO(__getlogin) \ - MACRO(__workq_kernreturn) \ - MACRO(start_wqthread) \ - /* pthreads interface functions */ \ - MACRO(pthread_cond_wait) \ - MACRO(pthread_cond_timedwait) \ - MACRO(pthread_cond_timedwait_relative_np) \ - MACRO(pthread_create) \ - MACRO(pthread_join) \ - MACRO(pthread_mutex_init) \ - MACRO(pthread_mutex_destroy) \ - MACRO(pthread_mutex_lock) \ - MACRO(pthread_mutex_trylock) \ - MACRO(pthread_mutex_unlock) \ - /* C Library functions */ \ - MACRO(dlclose) \ - MACRO(dlopen) \ - MACRO(dlsym) \ - MACRO(fclose) \ - MACRO(fopen) \ - MACRO(fread) \ - MACRO(fseek) \ - MACRO(ftell) \ - MACRO(fwrite) \ - MACRO(getenv) \ - MACRO(localtime_r) \ - MACRO(gmtime_r) \ - MACRO(localtime) \ - MACRO(gmtime) \ - MACRO(mktime) \ - MACRO(setlocale) \ - MACRO(strftime) \ - MACRO(arc4random) \ - MACRO(mach_absolute_time) \ - MACRO(mach_msg) \ - MACRO(mach_timebase_info) \ - MACRO(mach_vm_allocate) \ - MACRO(mach_vm_deallocate) \ - MACRO(mach_vm_protect) \ - MACRO(sandbox_free_error) \ - MACRO(sandbox_init) \ - MACRO(sandbox_init_with_parameters) \ - MACRO(task_threads) \ - MACRO(vm_copy) \ - MACRO(vm_purgable_control) \ - MACRO(tzset) \ - /* NSPR functions */ \ - MACRO(PL_NewHashTable) \ - MACRO(PL_HashTableDestroy) \ - /* Objective C functions */ \ - MACRO(class_getClassMethod) \ - MACRO(class_getInstanceMethod) \ - MACRO(method_exchangeImplementations) \ - MACRO(objc_autoreleasePoolPop) \ - MACRO(objc_autoreleasePoolPush) \ - MACRO(objc_msgSend) \ - /* Cocoa functions */ \ - MACRO(AcquireFirstMatchingEventInQueue) \ - MACRO(CFArrayAppendValue) \ - MACRO(CFArrayCreate) \ - MACRO(CFArrayGetCount) \ - MACRO(CFArrayGetValueAtIndex) \ - MACRO(CFArrayRemoveValueAtIndex) \ - MACRO(CFAttributedStringCreate) \ - MACRO(CFBundleCopyExecutableURL) \ - MACRO(CFBundleCopyInfoDictionaryForURL) \ - MACRO(CFBundleCreate) \ - MACRO(CFBundleGetBundleWithIdentifier) \ - MACRO(CFBundleGetDataPointerForName) \ - MACRO(CFBundleGetFunctionPointerForName) \ - MACRO(CFBundleGetIdentifier) \ - MACRO(CFBundleGetInfoDictionary) \ - MACRO(CFBundleGetMainBundle) \ - MACRO(CFBundleGetValueForInfoDictionaryKey) \ - MACRO(CFDataGetBytePtr) \ - MACRO(CFDataGetLength) \ - MACRO(CFDateFormatterCreate) \ - MACRO(CFDateFormatterGetFormat) \ - MACRO(CFDictionaryAddValue) \ - MACRO(CFDictionaryCreate) \ - MACRO(CFDictionaryCreateMutable) \ - MACRO(CFDictionaryCreateMutableCopy) \ - MACRO(CFDictionaryGetValue) \ - MACRO(CFDictionaryGetValueIfPresent) \ - MACRO(CFDictionaryReplaceValue) \ - MACRO(CFEqual) \ - MACRO(CFGetTypeID) \ - MACRO(CFLocaleCopyCurrent) \ - MACRO(CFLocaleCopyPreferredLanguages) \ - MACRO(CFLocaleCreate) \ - MACRO(CFLocaleGetIdentifier) \ - MACRO(CFNotificationCenterAddObserver) \ - MACRO(CFNotificationCenterGetLocalCenter) \ - MACRO(CFNotificationCenterRemoveObserver) \ - MACRO(CFNumberCreate) \ - MACRO(CFNumberGetValue) \ - MACRO(CFNumberIsFloatType) \ - MACRO(CFPreferencesAppValueIsForced) \ - MACRO(CFPreferencesCopyAppValue) \ - MACRO(CFPreferencesCopyValue) \ - MACRO(CFPropertyListCreateFromXMLData) \ - MACRO(CFPropertyListCreateWithStream) \ - MACRO(CFReadStreamClose) \ - MACRO(CFReadStreamCreateWithFile) \ - MACRO(CFReadStreamOpen) \ - MACRO(CFRelease) \ - MACRO(CFRetain) \ - MACRO(CFRunLoopAddSource) \ - MACRO(CFRunLoopGetCurrent) \ - MACRO(CFRunLoopRemoveSource) \ - MACRO(CFRunLoopSourceCreate) \ - MACRO(CFRunLoopSourceSignal) \ - MACRO(CFRunLoopWakeUp) \ - MACRO(CFStringAppendCharacters) \ - MACRO(CFStringCompare) \ - MACRO(CFStringCreateArrayBySeparatingStrings) \ - MACRO(CFStringCreateMutable) \ - MACRO(CFStringCreateWithBytes) \ - MACRO(CFStringCreateWithBytesNoCopy) \ - MACRO(CFStringCreateWithCharactersNoCopy) \ - MACRO(CFStringCreateWithCString) \ - MACRO(CFStringCreateWithFormat) \ - MACRO(CFStringGetBytes) \ - MACRO(CFStringGetCharacters) \ - MACRO(CFStringGetCString) \ - MACRO(CFStringGetCStringPtr) \ - MACRO(CFStringGetIntValue) \ - MACRO(CFStringGetLength) \ - MACRO(CFStringGetMaximumSizeForEncoding) \ - MACRO(CFStringHasPrefix) \ - MACRO(CFStringTokenizerAdvanceToNextToken) \ - MACRO(CFStringTokenizerCreate) \ - MACRO(CFStringTokenizerGetCurrentTokenRange) \ - MACRO(CFURLCreateFromFileSystemRepresentation) \ - MACRO(CFURLCreateFromFSRef) \ - MACRO(CFURLCreateWithFileSystemPath) \ - MACRO(CFURLCreateWithString) \ - MACRO(CFURLGetFileSystemRepresentation) \ - MACRO(CFURLGetFSRef) \ - MACRO(CFUUIDCreate) \ - MACRO(CFUUIDCreateString) \ - MACRO(CFUUIDGetUUIDBytes) \ - MACRO(CGAffineTransformConcat) \ - MACRO(CGBitmapContextCreateImage) \ - MACRO(CGBitmapContextCreateWithData) \ - MACRO(CGBitmapContextGetBytesPerRow) \ - MACRO(CGBitmapContextGetHeight) \ - MACRO(CGBitmapContextGetWidth) \ - MACRO(CGColorRelease) \ - MACRO(CGColorSpaceCopyICCProfile) \ - MACRO(CGFontCreateCopyWithVariations) \ - MACRO(CGColorSpaceCreateDeviceRGB) \ - MACRO(CGColorSpaceCreatePattern) \ - MACRO(CGColorSpaceRelease) \ - MACRO(CGContextBeginTransparencyLayerWithRect) \ - MACRO(CGContextClipToRects) \ - MACRO(CGContextConcatCTM) \ - MACRO(CGContextDrawImage) \ - MACRO(CGContextEndTransparencyLayer) \ - MACRO(CGContextFillRect) \ - MACRO(CGContextGetClipBoundingBox) \ - MACRO(CGContextGetCTM) \ - MACRO(CGContextGetType) \ - MACRO(CGContextGetUserSpaceToDeviceSpaceTransform) \ - MACRO(CGContextRestoreGState) \ - MACRO(CGContextSaveGState) \ - MACRO(CGContextSetAllowsFontSubpixelPositioning) \ - MACRO(CGContextSetAllowsFontSubpixelQuantization) \ - MACRO(CGContextSetAlpha) \ - MACRO(CGContextSetBaseCTM) \ - MACRO(CGContextSetCTM) \ - MACRO(CGContextSetGrayFillColor) \ - MACRO(CGContextSetRGBFillColor) \ - MACRO(CGContextSetShouldAntialias) \ - MACRO(CGContextSetShouldSmoothFonts) \ - MACRO(CGContextSetShouldSubpixelPositionFonts) \ - MACRO(CGContextSetShouldSubpixelQuantizeFonts) \ - MACRO(CGContextSetTextDrawingMode) \ - MACRO(CGContextSetTextMatrix) \ - MACRO(CGContextScaleCTM) \ - MACRO(CGContextTranslateCTM) \ - MACRO(CGDataProviderCreateWithData) \ - MACRO(CGDisplayCopyColorSpace) \ - MACRO(CGDisplayIOServicePort) \ - MACRO(CGEventSourceCounterForEventType) \ - MACRO(CGFontCopyTableForTag) \ - MACRO(CGFontCopyTableTags) \ - MACRO(CGFontCopyVariations) \ - MACRO(CGFontCreateWithDataProvider) \ - MACRO(CGFontCreateWithFontName) \ - MACRO(CGFontCreateWithPlatformFont) \ - MACRO(CGFontGetAscent) \ - MACRO(CGFontGetCapHeight) \ - MACRO(CGFontGetDescent) \ - MACRO(CGFontGetFontBBox) \ - MACRO(CGFontGetGlyphAdvances) \ - MACRO(CGFontGetGlyphBBoxes) \ - MACRO(CGFontGetGlyphPath) \ - MACRO(CGFontGetLeading) \ - MACRO(CGFontGetUnitsPerEm) \ - MACRO(CGFontGetXHeight) \ - MACRO(CGImageGetHeight) \ - MACRO(CGImageGetWidth) \ - MACRO(CGImageRelease) \ - MACRO(CGMainDisplayID) \ - MACRO(CGPathAddPath) \ - MACRO(CGPathApply) \ - MACRO(CGPathContainsPoint) \ - MACRO(CGPathCreateMutable) \ - MACRO(CGPathGetBoundingBox) \ - MACRO(CGPathGetCurrentPoint) \ - MACRO(CGPathIsEmpty) \ - MACRO(CGSSetDebugOptions) \ - MACRO(CGSShutdownServerConnections) \ - MACRO(CTFontCopyFamilyName) \ - MACRO(CTFontCopyFontDescriptor) \ - MACRO(CTFontCopyGraphicsFont) \ - MACRO(CTFontCopyTable) \ - MACRO(CTFontCopyVariationAxes) \ - MACRO(CTFontCreateForString) \ - MACRO(CTFontCreatePathForGlyph) \ - MACRO(CTFontCreateWithFontDescriptor) \ - MACRO(CTFontCreateWithGraphicsFont) \ - MACRO(CTFontCreateWithName) \ - MACRO(CTFontDescriptorCopyAttribute) \ - MACRO(CTFontDescriptorCreateCopyWithAttributes) \ - MACRO(CTFontDescriptorCreateMatchingFontDescriptors) \ - MACRO(CTFontDescriptorCreateWithAttributes) \ - MACRO(CTFontDrawGlyphs) \ - MACRO(CTFontGetAdvancesForGlyphs) \ - MACRO(CTFontGetAscent) \ - MACRO(CTFontGetBoundingBox) \ - MACRO(CTFontGetBoundingRectsForGlyphs) \ - MACRO(CTFontGetCapHeight) \ - MACRO(CTFontGetDescent) \ - MACRO(CTFontGetGlyphCount) \ - MACRO(CTFontGetGlyphsForCharacters) \ - MACRO(CTFontGetLeading) \ - MACRO(CTFontGetSize) \ - MACRO(CTFontGetSymbolicTraits) \ - MACRO(CTFontGetUnderlinePosition) \ - MACRO(CTFontGetUnderlineThickness) \ - MACRO(CTFontGetUnitsPerEm) \ - MACRO(CTFontGetXHeight) \ - MACRO(CTFontManagerCopyAvailableFontFamilyNames) \ - MACRO(CTFontManagerRegisterFontsForURLs) \ - MACRO(CTFontManagerSetAutoActivationSetting) \ - MACRO(CTLineCreateWithAttributedString) \ - MACRO(CTLineGetGlyphRuns) \ - MACRO(CTRunGetAttributes) \ - MACRO(CTRunGetGlyphCount) \ - MACRO(CTRunGetGlyphsPtr) \ - MACRO(CTRunGetPositionsPtr) \ - MACRO(CTRunGetStringIndicesPtr) \ - MACRO(CTRunGetStringRange) \ - MACRO(CTRunGetTypographicBounds) \ - MACRO(CUIDraw) \ - MACRO(FSCompareFSRefs) \ - MACRO(FSGetVolumeInfo) \ - MACRO(FSFindFolder) \ - MACRO(Gestalt) \ - MACRO(GetEventClass) \ - MACRO(GetCurrentEventQueue) \ - MACRO(GetCurrentProcess) \ - MACRO(GetEventAttributes) \ - MACRO(GetEventDispatcherTarget) \ - MACRO(GetEventKind) \ - MACRO(HIThemeDrawButton) \ - MACRO(HIThemeDrawFrame) \ - MACRO(HIThemeDrawGroupBox) \ - MACRO(HIThemeDrawGrowBox) \ - MACRO(HIThemeDrawMenuBackground) \ - MACRO(HIThemeDrawMenuItem) \ - MACRO(HIThemeDrawMenuSeparator) \ - MACRO(HIThemeDrawSeparator) \ - MACRO(HIThemeDrawTabPane) \ - MACRO(HIThemeDrawTrack) \ - MACRO(HIThemeGetGrowBoxBounds) \ - MACRO(HIThemeSetFill) \ - MACRO(IORegistryEntrySearchCFProperty) \ - MACRO(LSCopyAllHandlersForURLScheme) \ - MACRO(LSCopyApplicationForMIMEType) \ - MACRO(LSCopyItemAttribute) \ - MACRO(LSCopyKindStringForMIMEType) \ - MACRO(LSGetApplicationForInfo) \ - MACRO(LSGetApplicationForURL) \ - MACRO(NSClassFromString) \ - MACRO(NSRectFill) \ - MACRO(NSSearchPathForDirectoriesInDomains) \ - MACRO(NSSetFocusRingStyle) \ - MACRO(NSTemporaryDirectory) \ - MACRO(OSSpinLockLock) \ - MACRO(ReleaseEvent) \ - MACRO(RemoveEventFromQueue) \ - MACRO(RetainEvent) \ - MACRO(SendEventToEventTarget) \ - MACRO(SLDisplayCopyColorSpace) \ - MACRO(SLDisplayIOServicePort) \ - MACRO(SLEventSourceCounterForEventType) \ - MACRO(SLMainDisplayID) \ - MACRO(SLSSetDenyWindowServerConnections) \ +// Specify every function that is being redirected. MACRO is invoked with the +// function's name, followed by any hooks associated with the redirection for +// saving its output or adding a preamble. +#define FOR_EACH_REDIRECTION(MACRO) \ + /* System call wrappers */ \ + MACRO(kevent, RR_SaveRvalHadErrorNegative>) \ + MACRO(kevent64, RR_SaveRvalHadErrorNegative>) \ + MACRO(mprotect, nullptr, Preamble_mprotect) \ + MACRO(mmap, nullptr, Preamble_mmap) \ + MACRO(munmap, nullptr, Preamble_munmap) \ + MACRO(read, RR_SaveRvalHadErrorNegative>) \ + MACRO(__read_nocancel, RR_SaveRvalHadErrorNegative>) \ + MACRO(pread, RR_SaveRvalHadErrorNegative>) \ + MACRO(write, RR_SaveRvalHadErrorNegative) \ + MACRO(__write_nocancel, RR_SaveRvalHadErrorNegative) \ + MACRO(open, RR_SaveRvalHadErrorNegative) \ + MACRO(__open_nocancel, RR_SaveRvalHadErrorNegative) \ + MACRO(recv, RR_SaveRvalHadErrorNegative>) \ + MACRO(recvmsg, RR_SaveRvalHadErrorNegative) \ + MACRO(sendmsg, RR_SaveRvalHadErrorNegative) \ + MACRO(shm_open, RR_SaveRvalHadErrorNegative) \ + MACRO(socket, RR_SaveRvalHadErrorNegative) \ + MACRO(kqueue, RR_SaveRvalHadErrorNegative) \ + MACRO(pipe, RR_SaveRvalHadErrorNegative>) \ + MACRO(close, RR_SaveRvalHadErrorNegative) \ + MACRO(__close_nocancel, RR_SaveRvalHadErrorNegative) \ + MACRO(mkdir, RR_SaveRvalHadErrorNegative) \ + MACRO(dup, RR_SaveRvalHadErrorNegative) \ + MACRO(access, RR_SaveRvalHadErrorNegative) \ + MACRO(lseek, RR_SaveRvalHadErrorNegative) \ + MACRO(socketpair, RR_SaveRvalHadErrorNegative>) \ + MACRO(fileport_makeport, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(getsockopt, RR_SaveRvalHadErrorNegative) \ + MACRO(gettimeofday, RR_SaveRvalHadErrorNegative, \ + RR_WriteOptionalBufferFixedSize<1, sizeof(struct timezone)>>>, \ + Preamble_gettimeofday) \ + MACRO(getuid, RR_ScalarRval) \ + MACRO(geteuid, RR_ScalarRval) \ + MACRO(getgid, RR_ScalarRval) \ + MACRO(getegid, RR_ScalarRval) \ + MACRO(issetugid, RR_ScalarRval) \ + MACRO(__gettid, RR_ScalarRval) \ + MACRO(getpid, RR_ScalarRval) \ + MACRO(fcntl, RR_SaveRvalHadErrorNegative, Preamble_fcntl) \ + MACRO(getattrlist, RR_SaveRvalHadErrorNegative>) \ + MACRO(fstat$INODE64, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(lstat$INODE64, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(stat$INODE64, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(statfs$INODE64, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(fstatfs$INODE64, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(readlink, RR_SaveRvalHadErrorNegative>) \ + MACRO(__getdirentries64, RR_SaveRvalHadErrorNegative, \ + RR_WriteBufferFixedSize<3, sizeof(size_t)>>>) \ + MACRO(getdirentriesattr, RR_SaveRvalHadErrorNegative, \ + RR_WriteBuffer<2, 3>, \ + RR_WriteBufferFixedSize<4, sizeof(size_t)>, \ + RR_WriteBufferFixedSize<5, sizeof(size_t)>, \ + RR_WriteBufferFixedSize<6, sizeof(size_t)>>>) \ + MACRO(getrusage, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(__getrlimit, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(__setrlimit, RR_SaveRvalHadErrorNegative) \ + MACRO(sigprocmask, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(sigaltstack, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(sigaction, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(__pthread_sigmask, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(__fsgetpath, RR_SaveRvalHadErrorNegative>) \ + MACRO(__disable_threadsignal, nullptr, Preamble___disable_threadsignal) \ + MACRO(__sysctl, RR_SaveRvalHadErrorNegative) \ + MACRO(__mac_syscall, RR_SaveRvalHadErrorNegative) \ + MACRO(getaudit_addr, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(umask, RR_ScalarRval) \ + MACRO(__select, RR_SaveRvalHadErrorNegative, \ + RR_WriteBufferFixedSize<2, sizeof(fd_set)>, \ + RR_WriteBufferFixedSize<3, sizeof(fd_set)>, \ + RR_WriteOptionalBufferFixedSize<4, sizeof(timeval)>>>) \ + MACRO(__process_policy, RR_SaveRvalHadErrorNegative) \ + MACRO(__kdebug_trace, RR_SaveRvalHadErrorNegative) \ + MACRO(guarded_kqueue_np, \ + RR_SaveRvalHadErrorNegative>) \ + MACRO(csops, RR_SaveRvalHadErrorNegative>) \ + MACRO(__getlogin, RR_SaveRvalHadErrorNegative>) \ + MACRO(__workq_kernreturn, nullptr, Preamble___workq_kernreturn) \ + MACRO(start_wqthread, nullptr, Preamble_start_wqthread) \ + /* pthreads interface functions */ \ + MACRO(pthread_cond_wait, nullptr, Preamble_pthread_cond_wait) \ + MACRO(pthread_cond_timedwait, nullptr, Preamble_pthread_cond_timedwait) \ + MACRO(pthread_cond_timedwait_relative_np, nullptr, Preamble_pthread_cond_timedwait_relative_np) \ + MACRO(pthread_create, nullptr, Preamble_pthread_create) \ + MACRO(pthread_join, nullptr, Preamble_pthread_join) \ + MACRO(pthread_mutex_init, nullptr, Preamble_pthread_mutex_init) \ + MACRO(pthread_mutex_destroy, nullptr, Preamble_pthread_mutex_destroy) \ + MACRO(pthread_mutex_lock, nullptr, Preamble_pthread_mutex_lock) \ + MACRO(pthread_mutex_trylock, nullptr, Preamble_pthread_mutex_trylock) \ + MACRO(pthread_mutex_unlock, nullptr, Preamble_pthread_mutex_unlock) \ + /* C Library functions */ \ + MACRO(dlclose, nullptr, Preamble_Veto<0>) \ + MACRO(dlopen, nullptr, Preamble_PassThrough) \ + MACRO(dlsym, nullptr, Preamble_PassThrough) \ + MACRO(fclose, RR_SaveRvalHadErrorNegative) \ + MACRO(fopen, RR_SaveRvalHadErrorZero) \ + MACRO(fread, RR_Compose) \ + MACRO(fseek, RR_SaveRvalHadErrorNegative) \ + MACRO(ftell, RR_SaveRvalHadErrorNegative) \ + MACRO(fwrite, RR_ScalarRval) \ + MACRO(getenv, RR_CStringRval) \ + MACRO(localtime_r, RR_SaveRvalHadErrorZero, \ + RR_RvalIsArgument<1>>>) \ + MACRO(gmtime_r, RR_SaveRvalHadErrorZero, \ + RR_RvalIsArgument<1>>>) \ + MACRO(localtime, nullptr, Preamble_localtime) \ + MACRO(gmtime, nullptr, Preamble_gmtime) \ + MACRO(mktime, RR_Compose>) \ + MACRO(setlocale, RR_CStringRval) \ + MACRO(strftime, RR_Compose>) \ + MACRO(arc4random, RR_ScalarRval) \ + MACRO(mach_absolute_time, RR_ScalarRval, Preamble_mach_absolute_time) \ + MACRO(mach_msg, RR_Compose>) \ + MACRO(mach_timebase_info, \ + RR_Compose>) \ + MACRO(mach_vm_allocate, nullptr, Preamble_mach_vm_allocate) \ + MACRO(mach_vm_deallocate, nullptr, Preamble_mach_vm_deallocate) \ + MACRO(mach_vm_protect, nullptr, Preamble_mach_vm_protect) \ + /* By passing through events when initializing the sandbox, we ensure both */ \ + /* that we actually initialize the process sandbox while replaying as well as */ \ + /* while recording, and that any activity in these calls does not interfere */ \ + /* with the replay. */ \ + MACRO(sandbox_init, nullptr, Preamble_PassThrough) \ + MACRO(sandbox_init_with_parameters, nullptr, Preamble_PassThrough) \ + /* Make sure events are passed through here so that replaying processes can */ \ + /* inspect their own threads. */ \ + MACRO(task_threads, nullptr, Preamble_PassThrough) \ + MACRO(vm_copy, nullptr, Preamble_vm_copy) \ + MACRO(vm_purgable_control, nullptr, Preamble_vm_purgable_control) \ + MACRO(tzset) \ + /* NSPR functions */ \ + MACRO(PL_NewHashTable, nullptr, Preamble_PL_NewHashTable) \ + MACRO(PL_HashTableDestroy, nullptr, Preamble_PL_HashTableDestroy) \ + /* Objective C functions */ \ + MACRO(class_getClassMethod, RR_ScalarRval) \ + MACRO(class_getInstanceMethod, RR_ScalarRval) \ + MACRO(method_exchangeImplementations) \ + MACRO(objc_autoreleasePoolPop) \ + MACRO(objc_autoreleasePoolPush, RR_ScalarRval) \ + MACRO(objc_msgSend, nullptr, Preamble_objc_msgSend) \ + /* Cocoa functions */ \ + MACRO(AcquireFirstMatchingEventInQueue, RR_ScalarRval) \ + MACRO(CFArrayAppendValue) \ + MACRO(CFArrayCreate, RR_ScalarRval) \ + MACRO(CFArrayGetCount, RR_ScalarRval) \ + MACRO(CFArrayGetValueAtIndex, RR_ScalarRval) \ + MACRO(CFArrayRemoveValueAtIndex) \ + MACRO(CFAttributedStringCreate, RR_ScalarRval) \ + MACRO(CFBundleCopyExecutableURL, RR_ScalarRval) \ + MACRO(CFBundleCopyInfoDictionaryForURL, RR_ScalarRval) \ + MACRO(CFBundleCreate, RR_ScalarRval) \ + MACRO(CFBundleGetBundleWithIdentifier, RR_ScalarRval) \ + MACRO(CFBundleGetDataPointerForName, nullptr, Preamble_VetoIfNotPassedThrough<0>) \ + MACRO(CFBundleGetFunctionPointerForName, nullptr, Preamble_VetoIfNotPassedThrough<0>) \ + MACRO(CFBundleGetIdentifier, RR_ScalarRval) \ + MACRO(CFBundleGetInfoDictionary, RR_ScalarRval) \ + MACRO(CFBundleGetMainBundle, RR_ScalarRval) \ + MACRO(CFBundleGetValueForInfoDictionaryKey, RR_ScalarRval) \ + MACRO(CFDataGetBytePtr, RR_CFDataGetBytePtr) \ + MACRO(CFDataGetLength, RR_ScalarRval) \ + MACRO(CFDateFormatterCreate, RR_ScalarRval) \ + MACRO(CFDateFormatterGetFormat, RR_ScalarRval) \ + MACRO(CFDictionaryAddValue) \ + MACRO(CFDictionaryCreate, RR_ScalarRval) \ + MACRO(CFDictionaryCreateMutable, RR_ScalarRval) \ + MACRO(CFDictionaryCreateMutableCopy, RR_ScalarRval) \ + MACRO(CFDictionaryGetValue, RR_ScalarRval) \ + MACRO(CFDictionaryGetValueIfPresent, \ + RR_Compose>) \ + MACRO(CFDictionaryReplaceValue) \ + MACRO(CFEqual, RR_ScalarRval) \ + MACRO(CFGetTypeID, RR_ScalarRval) \ + MACRO(CFLocaleCopyCurrent, RR_ScalarRval) \ + MACRO(CFLocaleCopyPreferredLanguages, RR_ScalarRval) \ + MACRO(CFLocaleCreate, RR_ScalarRval) \ + MACRO(CFLocaleGetIdentifier, RR_ScalarRval) \ + MACRO(CFNotificationCenterAddObserver, nullptr, Preamble_CFNotificationCenterAddObserver) \ + MACRO(CFNotificationCenterGetLocalCenter, RR_ScalarRval) \ + MACRO(CFNotificationCenterRemoveObserver) \ + MACRO(CFNumberCreate, RR_ScalarRval) \ + MACRO(CFNumberGetValue, RR_Compose) \ + MACRO(CFNumberIsFloatType, RR_ScalarRval) \ + MACRO(CFPreferencesAppValueIsForced, RR_ScalarRval) \ + MACRO(CFPreferencesCopyAppValue, RR_ScalarRval) \ + MACRO(CFPreferencesCopyValue, RR_ScalarRval) \ + MACRO(CFPropertyListCreateFromXMLData, RR_ScalarRval) \ + MACRO(CFPropertyListCreateWithStream, RR_ScalarRval) \ + MACRO(CFReadStreamClose) \ + MACRO(CFReadStreamCreateWithFile, RR_ScalarRval) \ + MACRO(CFReadStreamOpen, RR_ScalarRval) \ + MACRO(CFRelease, RR_ScalarRval) \ + MACRO(CFRetain, RR_ScalarRval) \ + MACRO(CFRunLoopAddSource) \ + MACRO(CFRunLoopGetCurrent, RR_ScalarRval) \ + MACRO(CFRunLoopRemoveSource) \ + MACRO(CFRunLoopSourceCreate, RR_ScalarRval, Preamble_CFRunLoopSourceCreate) \ + MACRO(CFRunLoopSourceSignal) \ + MACRO(CFRunLoopWakeUp) \ + MACRO(CFStringAppendCharacters) \ + MACRO(CFStringCompare, RR_ScalarRval) \ + MACRO(CFStringCreateArrayBySeparatingStrings, RR_ScalarRval) \ + MACRO(CFStringCreateMutable, RR_ScalarRval) \ + MACRO(CFStringCreateWithBytes, RR_ScalarRval) \ + MACRO(CFStringCreateWithBytesNoCopy, RR_ScalarRval) \ + MACRO(CFStringCreateWithCharactersNoCopy, RR_ScalarRval) \ + MACRO(CFStringCreateWithCString, RR_ScalarRval) \ + MACRO(CFStringCreateWithFormat, RR_ScalarRval) \ + /* Argument indexes are off by one here as the CFRange argument uses two slots. */ \ + MACRO(CFStringGetBytes, RR_Compose< \ + RR_ScalarRval, \ + RR_WriteOptionalBuffer<6, 7>, \ + RR_WriteOptionalBufferFixedSize<8, sizeof(CFIndex)>>) \ + /* Argument indexes are off by one here as the CFRange argument uses two slots. */ \ + /* We also need to specify the argument register with the range's length here. */ \ + MACRO(CFStringGetCharacters, RR_WriteBuffer<3, 2, UniChar>) \ + MACRO(CFStringGetCString, RR_Compose>) \ + MACRO(CFStringGetCStringPtr, nullptr, Preamble_VetoIfNotPassedThrough<0>) \ + MACRO(CFStringGetIntValue, RR_ScalarRval) \ + MACRO(CFStringGetLength, RR_ScalarRval) \ + MACRO(CFStringGetMaximumSizeForEncoding, RR_ScalarRval) \ + MACRO(CFStringHasPrefix, RR_ScalarRval) \ + MACRO(CFStringTokenizerAdvanceToNextToken, RR_ScalarRval) \ + MACRO(CFStringTokenizerCreate, RR_ScalarRval) \ + MACRO(CFStringTokenizerGetCurrentTokenRange, RR_ComplexScalarRval) \ + MACRO(CFURLCreateFromFileSystemRepresentation, RR_ScalarRval) \ + MACRO(CFURLCreateFromFSRef, RR_ScalarRval) \ + MACRO(CFURLCreateWithFileSystemPath, RR_ScalarRval) \ + MACRO(CFURLCreateWithString, RR_ScalarRval) \ + MACRO(CFURLGetFileSystemRepresentation, RR_Compose>) \ + MACRO(CFURLGetFSRef, RR_Compose>) \ + MACRO(CFUUIDCreate, RR_ScalarRval) \ + MACRO(CFUUIDCreateString, RR_ScalarRval) \ + MACRO(CFUUIDGetUUIDBytes, RR_ComplexScalarRval) \ + MACRO(CGAffineTransformConcat, RR_OversizeRval) \ + MACRO(CGBitmapContextCreateImage, RR_ScalarRval) \ + MACRO(CGBitmapContextCreateWithData, \ + RR_Compose) \ + MACRO(CGBitmapContextGetBytesPerRow, RR_ScalarRval) \ + MACRO(CGBitmapContextGetHeight, RR_ScalarRval) \ + MACRO(CGBitmapContextGetWidth, RR_ScalarRval) \ + MACRO(CGColorRelease, RR_ScalarRval) \ + MACRO(CGColorSpaceCopyICCProfile, RR_ScalarRval) \ + MACRO(CGColorSpaceCreateDeviceRGB, RR_ScalarRval) \ + MACRO(CGColorSpaceCreatePattern, RR_ScalarRval) \ + MACRO(CGColorSpaceRelease, RR_ScalarRval) \ + MACRO(CGContextBeginTransparencyLayerWithRect) \ + MACRO(CGContextClipToRects, RR_ScalarRval) \ + MACRO(CGContextConcatCTM) \ + MACRO(CGContextDrawImage, RR_FlushCGContext<0>) \ + MACRO(CGContextEndTransparencyLayer) \ + MACRO(CGContextFillRect, RR_FlushCGContext<0>) \ + MACRO(CGContextGetClipBoundingBox, RR_OversizeRval) \ + MACRO(CGContextGetCTM, RR_OversizeRval) \ + MACRO(CGContextGetType, RR_ScalarRval) \ + MACRO(CGContextGetUserSpaceToDeviceSpaceTransform, RR_OversizeRval) \ + MACRO(CGContextRestoreGState, nullptr, Preamble_CGContextRestoreGState) \ + MACRO(CGContextSaveGState) \ + MACRO(CGContextSetAllowsFontSubpixelPositioning) \ + MACRO(CGContextSetAllowsFontSubpixelQuantization) \ + MACRO(CGContextSetAlpha) \ + MACRO(CGContextSetBaseCTM) \ + MACRO(CGContextSetCTM) \ + MACRO(CGContextSetGrayFillColor) \ + MACRO(CGContextSetRGBFillColor) \ + MACRO(CGContextSetShouldAntialias) \ + MACRO(CGContextSetShouldSmoothFonts) \ + MACRO(CGContextSetShouldSubpixelPositionFonts) \ + MACRO(CGContextSetShouldSubpixelQuantizeFonts) \ + MACRO(CGContextSetTextDrawingMode) \ + MACRO(CGContextSetTextMatrix) \ + MACRO(CGContextScaleCTM) \ + MACRO(CGContextTranslateCTM) \ + MACRO(CGDataProviderCreateWithData, RR_Compose) \ + MACRO(CGDisplayCopyColorSpace, RR_ScalarRval) \ + MACRO(CGDisplayIOServicePort, RR_ScalarRval) \ + MACRO(CGEventSourceCounterForEventType, RR_ScalarRval) \ + MACRO(CGFontCopyTableForTag, RR_ScalarRval) \ + MACRO(CGFontCopyTableTags, RR_ScalarRval) \ + MACRO(CGFontCopyVariations, RR_ScalarRval) \ + MACRO(CGFontCreateCopyWithVariations, RR_ScalarRval) \ + MACRO(CGFontCreateWithDataProvider, RR_ScalarRval) \ + MACRO(CGFontCreateWithFontName, RR_ScalarRval) \ + MACRO(CGFontCreateWithPlatformFont, RR_ScalarRval) \ + MACRO(CGFontGetAscent, RR_ScalarRval) \ + MACRO(CGFontGetCapHeight, RR_ScalarRval) \ + MACRO(CGFontGetDescent, RR_ScalarRval) \ + MACRO(CGFontGetFontBBox, RR_OversizeRval) \ + MACRO(CGFontGetGlyphAdvances, RR_Compose>) \ + MACRO(CGFontGetGlyphBBoxes, RR_Compose>) \ + MACRO(CGFontGetGlyphPath, RR_ScalarRval) \ + MACRO(CGFontGetLeading, RR_ScalarRval) \ + MACRO(CGFontGetUnitsPerEm, RR_ScalarRval) \ + MACRO(CGFontGetXHeight, RR_ScalarRval) \ + MACRO(CGImageGetHeight, RR_ScalarRval) \ + MACRO(CGImageGetWidth, RR_ScalarRval) \ + MACRO(CGImageRelease, RR_ScalarRval) \ + MACRO(CGMainDisplayID, RR_ScalarRval) \ + MACRO(CGPathAddPath) \ + MACRO(CGPathApply, nullptr, Preamble_CGPathApply) \ + MACRO(CGPathContainsPoint, RR_ScalarRval) \ + MACRO(CGPathCreateMutable, RR_ScalarRval) \ + MACRO(CGPathGetBoundingBox, RR_OversizeRval) \ + MACRO(CGPathGetCurrentPoint, RR_ComplexFloatRval) \ + MACRO(CGPathIsEmpty, RR_ScalarRval) \ + MACRO(CGSSetDebugOptions, RR_ScalarRval) \ + MACRO(CGSShutdownServerConnections) \ + MACRO(CTFontCopyFamilyName, RR_ScalarRval) \ + MACRO(CTFontCopyFontDescriptor, RR_ScalarRval) \ + MACRO(CTFontCopyGraphicsFont, RR_ScalarRval) \ + MACRO(CTFontCopyTable, RR_ScalarRval) \ + MACRO(CTFontCopyVariationAxes, RR_ScalarRval) \ + MACRO(CTFontCreateForString, RR_ScalarRval) \ + MACRO(CTFontCreatePathForGlyph, RR_ScalarRval) \ + MACRO(CTFontCreateWithFontDescriptor, RR_ScalarRval) \ + MACRO(CTFontCreateWithGraphicsFont, RR_ScalarRval) \ + MACRO(CTFontCreateWithName, RR_ScalarRval) \ + MACRO(CTFontDescriptorCopyAttribute, RR_ScalarRval) \ + MACRO(CTFontDescriptorCreateCopyWithAttributes, RR_ScalarRval) \ + MACRO(CTFontDescriptorCreateMatchingFontDescriptors, RR_ScalarRval) \ + MACRO(CTFontDescriptorCreateWithAttributes, RR_ScalarRval) \ + MACRO(CTFontDrawGlyphs, RR_FlushCGContext<4>) \ + MACRO(CTFontGetAdvancesForGlyphs, \ + RR_Compose>) \ + MACRO(CTFontGetAscent, RR_FloatRval) \ + MACRO(CTFontGetBoundingBox, RR_OversizeRval) \ + MACRO(CTFontGetBoundingRectsForGlyphs, \ + /* Argument indexes here are off by one due to the oversize rval. */ \ + RR_Compose, RR_WriteOptionalBuffer<4, 5, CGRect>>) \ + MACRO(CTFontGetCapHeight, RR_FloatRval) \ + MACRO(CTFontGetDescent, RR_FloatRval) \ + MACRO(CTFontGetGlyphCount, RR_ScalarRval) \ + MACRO(CTFontGetGlyphsForCharacters, \ + RR_Compose>) \ + MACRO(CTFontGetLeading, RR_FloatRval) \ + MACRO(CTFontGetSize, RR_FloatRval) \ + MACRO(CTFontGetSymbolicTraits, RR_ScalarRval) \ + MACRO(CTFontGetUnderlinePosition, RR_FloatRval) \ + MACRO(CTFontGetUnderlineThickness, RR_FloatRval) \ + MACRO(CTFontGetUnitsPerEm, RR_ScalarRval) \ + MACRO(CTFontGetXHeight, RR_FloatRval) \ + MACRO(CTFontManagerCopyAvailableFontFamilyNames, RR_ScalarRval) \ + MACRO(CTFontManagerRegisterFontsForURLs, RR_ScalarRval) \ + MACRO(CTFontManagerSetAutoActivationSetting) \ + MACRO(CTLineCreateWithAttributedString, RR_ScalarRval) \ + MACRO(CTLineGetGlyphRuns, RR_ScalarRval) \ + MACRO(CTRunGetAttributes, RR_ScalarRval) \ + MACRO(CTRunGetGlyphCount, RR_ScalarRval) \ + MACRO(CTRunGetGlyphsPtr, RR_CTRunGetElements) \ + MACRO(CTRunGetPositionsPtr, RR_CTRunGetElements) \ + MACRO(CTRunGetStringIndicesPtr, RR_CTRunGetElements) \ + MACRO(CTRunGetStringRange, RR_ComplexScalarRval) \ + /* Argument indexes are off by one here as the CFRange argument uses two slots. */ \ + MACRO(CTRunGetTypographicBounds, RR_Compose< \ + RR_FloatRval, \ + RR_WriteOptionalBufferFixedSize<3, sizeof(CGFloat)>, \ + RR_WriteOptionalBufferFixedSize<4, sizeof(CGFloat)>, \ + RR_WriteOptionalBufferFixedSize<5, sizeof(CGFloat)>>) \ + MACRO(CUIDraw) \ + MACRO(FSCompareFSRefs, RR_ScalarRval) \ + MACRO(FSGetVolumeInfo, RR_Compose< \ + RR_ScalarRval, \ + RR_WriteBufferFixedSize<5, sizeof(HFSUniStr255)>, \ + RR_WriteBufferFixedSize<6, sizeof(FSRef)>>) \ + MACRO(FSFindFolder, RR_Compose>) \ + MACRO(Gestalt, RR_Compose>) \ + MACRO(GetEventClass, RR_ScalarRval) \ + MACRO(GetCurrentEventQueue, RR_ScalarRval) \ + MACRO(GetCurrentProcess, \ + RR_Compose>) \ + MACRO(GetEventAttributes, RR_ScalarRval) \ + MACRO(GetEventDispatcherTarget, RR_ScalarRval) \ + MACRO(GetEventKind, RR_ScalarRval) \ + MACRO(HIThemeDrawButton, RR_ScalarRval) \ + MACRO(HIThemeDrawFrame, RR_ScalarRval) \ + MACRO(HIThemeDrawGroupBox, RR_ScalarRval) \ + MACRO(HIThemeDrawGrowBox, RR_ScalarRval) \ + MACRO(HIThemeDrawMenuBackground, RR_ScalarRval) \ + MACRO(HIThemeDrawMenuItem, RR_ScalarRval) \ + MACRO(HIThemeDrawMenuSeparator, RR_ScalarRval) \ + MACRO(HIThemeDrawSeparator, RR_ScalarRval) \ + MACRO(HIThemeDrawTabPane, RR_ScalarRval) \ + MACRO(HIThemeDrawTrack, RR_ScalarRval) \ + MACRO(HIThemeGetGrowBoxBounds, \ + RR_Compose>) \ + MACRO(HIThemeSetFill, RR_ScalarRval) \ + MACRO(IORegistryEntrySearchCFProperty, RR_ScalarRval) \ + MACRO(LSCopyAllHandlersForURLScheme, RR_ScalarRval) \ + MACRO(LSCopyApplicationForMIMEType, \ + RR_Compose>) \ + MACRO(LSCopyItemAttribute, \ + RR_Compose>) \ + MACRO(LSCopyKindStringForMIMEType, \ + RR_Compose>) \ + MACRO(LSGetApplicationForInfo, RR_Compose< \ + RR_ScalarRval, \ + RR_WriteOptionalBufferFixedSize<4, sizeof(FSRef)>, \ + RR_WriteOptionalBufferFixedSize<5, sizeof(CFURLRef)>>) \ + MACRO(LSGetApplicationForURL, RR_Compose< \ + RR_ScalarRval, \ + RR_WriteOptionalBufferFixedSize<2, sizeof(FSRef)>, \ + RR_WriteOptionalBufferFixedSize<3, sizeof(CFURLRef)>>) \ + MACRO(NSClassFromString, RR_ScalarRval) \ + MACRO(NSRectFill) \ + MACRO(NSSearchPathForDirectoriesInDomains, RR_ScalarRval) \ + MACRO(NSSetFocusRingStyle, RR_ScalarRval) \ + MACRO(NSTemporaryDirectory, RR_ScalarRval) \ + MACRO(OSSpinLockLock, nullptr, Preamble_OSSpinLockLock) \ + MACRO(ReleaseEvent, RR_ScalarRval) \ + MACRO(RemoveEventFromQueue, RR_ScalarRval) \ + MACRO(RetainEvent, RR_ScalarRval) \ + MACRO(SendEventToEventTarget, RR_ScalarRval) \ + /* These are not public APIs, but other redirected functions may be aliases for */ \ + /* these which are dynamically installed on the first call in a way that our */ \ + /* redirection mechanism doesn't completely account for. */ \ + MACRO(SLDisplayCopyColorSpace, RR_ScalarRval) \ + MACRO(SLDisplayIOServicePort, RR_ScalarRval) \ + MACRO(SLEventSourceCounterForEventType, RR_ScalarRval) \ + MACRO(SLMainDisplayID, RR_ScalarRval) \ + MACRO(SLSSetDenyWindowServerConnections, RR_ScalarRval) \ MACRO(SLSShutdownServerConnections) -#define MAKE_CALL_EVENT(aName) CallEvent_ ##aName , +#define MAKE_CALL_EVENT(aName, ...) CallEvent_ ##aName , enum CallEvent { \ FOR_EACH_REDIRECTION(MAKE_CALL_EVENT) \ @@ -500,149 +574,71 @@ ReplayInvokeCallback(size_t aCallbackId) // system call redirections /////////////////////////////////////////////////////////////////////////////// -static ssize_t -RR_recv(int aSockFd, void* aBuf, size_t aLength, int aFlags) +static void +RR_recvmsg(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - RecordReplayFunction(recv, ssize_t, aSockFd, aBuf, aLength, aFlags); - events.CheckInput(aLength); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - MOZ_RELEASE_ASSERT((size_t) rval <= aLength); - events.RecordOrReplayBytes(aBuf, rval); - return rval; -} + auto& msg = aArguments->Arg<1, struct msghdr*>(); -static ssize_t -RR_recvmsg(int aSockFd, struct msghdr* aMsg, int aFlags) -{ - size_t* initialLengths = nullptr; - if (!AreThreadEventsPassedThrough()) { - initialLengths = new size_t[2 + aMsg->msg_iovlen]; - initialLengths[0] = aMsg->msg_controllen; - initialLengths[1] = aMsg->msg_iovlen; - for (int i = 0; i < aMsg->msg_iovlen; i++) { - initialLengths[i + 2] = aMsg->msg_iov[i].iov_len; - } + aEvents.CheckInput(msg->msg_iovlen); + for (int i = 0; i < msg->msg_iovlen; i++) { + aEvents.CheckInput(msg->msg_iov[i].iov_len); } - RecordReplayFunction(recvmsg, ssize_t, aSockFd, aMsg, aFlags); - - for (size_t i = 0; i < 2 + initialLengths[1]; i++) { - events.CheckInput(initialLengths[i]); - } - delete[] initialLengths; - - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - - events.RecordOrReplayValue(&aMsg->msg_flags); - events.RecordOrReplayValue(&aMsg->msg_controllen); - events.RecordOrReplayBytes(aMsg->msg_control, aMsg->msg_controllen); - - size_t nbytes = (size_t) rval; - events.RecordOrReplayValue(&aMsg->msg_iovlen); - for (int i = 0; i < aMsg->msg_iovlen && nbytes; i++) { - struct iovec* iov = &aMsg->msg_iov[i]; - events.RecordOrReplayValue(&iov->iov_len); + aEvents.RecordOrReplayValue(&msg->msg_flags); + aEvents.RecordOrReplayValue(&msg->msg_controllen); + aEvents.RecordOrReplayBytes(msg->msg_control, msg->msg_controllen); + size_t nbytes = aArguments->Rval(); + for (int i = 0; nbytes && i < msg->msg_iovlen; i++) { + struct iovec* iov = &msg->msg_iov[i]; size_t iovbytes = nbytes <= iov->iov_len ? nbytes : iov->iov_len; - events.RecordOrReplayBytes(iov->iov_base, iovbytes); + aEvents.RecordOrReplayBytes(iov->iov_base, iovbytes); nbytes -= iovbytes; } MOZ_RELEASE_ASSERT(nbytes == 0); - return rval; } -static ssize_t -RR_sendmsg(int aFd, struct msghdr* aMsg, int aFlags) -{ - RecordReplayFunction(sendmsg, ssize_t, aFd, aMsg, aFlags); - events.CheckInput(aMsg->msg_iovlen); - for (size_t i = 0; i < (size_t) aMsg->msg_iovlen; i++) { - events.CheckInput(aMsg->msg_iov[i].iov_len); - } - RecordOrReplayHadErrorNegative(rrf); - return rval; -} - -static ssize_t -RR_kevent(int aKq, const struct kevent* aChanges, int aChangeCount, - struct kevent* aEvents, int aEventCount, - const struct timespec* aTimeout) -{ - RecordReplayFunction(kevent, ssize_t, - aKq, aChanges, aChangeCount, aEvents, aEventCount, aTimeout); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - - // Copy the kevents verbatim. Note that kevent has an opaque pointer field - // |udata| whose value could potentially vary between recording and replay. - // Fortunately, all mozilla callers of this function only use scalar values - // in |udata| so we don't need to be smarter here. - events.CheckInput(aEventCount); - events.RecordOrReplayBytes(aEvents, aEventCount * sizeof(struct kevent)); - return rval; -} - -static ssize_t -RR_kevent64(int aKq, const struct kevent64_s* aChanges, int aChangeCount, - struct kevent64_s* aEvents, int aEventCount, unsigned int aFlags, - const struct timespec* aTimeout) -{ - RecordReplayFunction(kevent64, ssize_t, - aKq, aChanges, aChangeCount, aEvents, aEventCount, aFlags, aTimeout); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - - // Copy the kevents verbatim, as for kevent(). - events.CheckInput(aEventCount); - events.RecordOrReplayBytes(aEvents, aEventCount * sizeof(struct kevent64_s)); - return rval; -} - -static ssize_t -RR_madvise(void* aAddress, size_t aSize, int aFlags) -{ - // Don't do anything with madvise for now, it is called by the JS GC. - return OriginalCall(madvise, int, aAddress, aSize, aFlags); -} - -static ssize_t -RR_mprotect(void* aAddress, size_t aSize, int aFlags) +static PreambleResult +Preamble_mprotect(CallArguments* aArguments) { + // Ignore any mprotect calls that occur after saving a checkpoint. if (!HasSavedCheckpoint()) { - return OriginalCall(mprotect, int, aAddress, aSize, aFlags); + return PreambleResult::PassThrough; } - return 0; + aArguments->Rval() = 0; + return PreambleResult::Veto; } -static void* -RR_mmap(void* aAddress, size_t aSize, int aProt, int aFlags, int aFd, off_t aOffset) +static PreambleResult +Preamble_mmap(CallArguments* aArguments) { - MOZ_RELEASE_ASSERT(aAddress == PageBase(aAddress)); + auto& address = aArguments->Arg<0, void*>(); + auto& size = aArguments->Arg<1, size_t>(); + auto& prot = aArguments->Arg<2, size_t>(); + auto& flags = aArguments->Arg<3, size_t>(); + auto& fd = aArguments->Arg<4, size_t>(); + auto& offset = aArguments->Arg<5, size_t>(); + + MOZ_RELEASE_ASSERT(address == PageBase(address)); // Make sure that fixed mappings do not interfere with snapshot state. - if (aFlags & MAP_FIXED) { - CheckFixedMemory(aAddress, RoundupSizeToPageBoundary(aSize)); + if (flags & MAP_FIXED) { + CheckFixedMemory(address, RoundupSizeToPageBoundary(size)); } void* memory = nullptr; - if ((aFlags & MAP_ANON) || (IsReplaying() && !AreThreadEventsPassedThrough())) { + if ((flags & MAP_ANON) || (IsReplaying() && !AreThreadEventsPassedThrough())) { // Get an anonymous mapping for the result. - if (aFlags & MAP_FIXED) { + if (flags & MAP_FIXED) { // For fixed allocations, make sure this memory region is mapped and zero. if (!HasSavedCheckpoint()) { // Make sure this memory region is writable. - OriginalCall(mprotect, int, aAddress, aSize, PROT_READ | PROT_WRITE | PROT_EXEC); + OriginalCall(mprotect, int, address, size, PROT_READ | PROT_WRITE | PROT_EXEC); } - memset(aAddress, 0, aSize); - memory = aAddress; + memset(address, 0, size); + memory = address; } else { - memory = AllocateMemoryTryAddress(aAddress, RoundupSizeToPageBoundary(aSize), + memory = AllocateMemoryTryAddress(address, RoundupSizeToPageBoundary(size), MemoryKind::Tracked); } } else { @@ -650,178 +646,71 @@ RR_mmap(void* aAddress, size_t aSize, int aProt, int aFlags, int aFd, off_t aOff // for memory that is already allocated. If we haven't saved a checkpoint // then this is no problem, but after saving a checkpoint we have to make // sure that protection flags are what we expect them to be. - int newProt = HasSavedCheckpoint() ? (PROT_READ | PROT_EXEC) : aProt; - memory = OriginalCall(mmap, void*, aAddress, aSize, newProt, aFlags, aFd, aOffset); + int newProt = HasSavedCheckpoint() ? (PROT_READ | PROT_EXEC) : prot; + memory = OriginalCall(mmap, void*, address, size, newProt, flags, fd, offset); - if (aFlags & MAP_FIXED) { - MOZ_RELEASE_ASSERT(memory == aAddress); - RestoreWritableFixedMemory(memory, RoundupSizeToPageBoundary(aSize)); + if (flags & MAP_FIXED) { + MOZ_RELEASE_ASSERT(memory == address); + RestoreWritableFixedMemory(memory, RoundupSizeToPageBoundary(size)); } else if (memory && memory != (void*)-1) { - RegisterAllocatedMemory(memory, RoundupSizeToPageBoundary(aSize), MemoryKind::Tracked); + RegisterAllocatedMemory(memory, RoundupSizeToPageBoundary(size), MemoryKind::Tracked); } } - if (!(aFlags & MAP_ANON) && !AreThreadEventsPassedThrough()) { + if (!(flags & MAP_ANON) && !AreThreadEventsPassedThrough()) { // Include the data just mapped in the recording. MOZ_RELEASE_ASSERT(memory && memory != (void*)-1); - RecordReplayBytes(memory, aSize); + RecordReplayBytes(memory, size); } - return memory; + aArguments->Rval() = memory; + return PreambleResult::Veto; } -static ssize_t -RR_munmap(void* aAddress, size_t aSize) +static PreambleResult +Preamble_munmap(CallArguments* aArguments) { - DeallocateMemory(aAddress, aSize, MemoryKind::Tracked); - return 0; + auto& address = aArguments->Arg<0, void*>(); + auto& size = aArguments->Arg<1, size_t>(); + + DeallocateMemory(address, size, MemoryKind::Tracked); + aArguments->Rval() = 0; + return PreambleResult::Veto; } -static ssize_t -RR_read(int aFd, void* aBuf, size_t aCount) +static void +RR_getsockopt(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - RecordReplayFunction(read, ssize_t, aFd, aBuf, aCount); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aCount); - MOZ_RELEASE_ASSERT((size_t)rval <= aCount); - events.RecordOrReplayBytes(aBuf, rval); - return rval; + auto& optval = aArguments->Arg<3, void*>(); + auto& optlen = aArguments->Arg<4, int*>(); + + // The initial length has already been clobbered while recording, but make + // sure there is enough room for the copy while replaying. + int initLen = *optlen; + aEvents.RecordOrReplayValue(optlen); + MOZ_RELEASE_ASSERT(*optlen <= initLen); + + aEvents.RecordOrReplayBytes(optval, *optlen); } -static ssize_t -RR___read_nocancel(int aFd, void* aBuf, size_t aCount) -{ - RecordReplayFunction(__read_nocancel, ssize_t, aFd, aBuf, aCount); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aCount); - MOZ_RELEASE_ASSERT((size_t)rval <= aCount); - events.RecordOrReplayBytes(aBuf, rval); - return rval; -} - -static ssize_t -RR_pread(int aFd, void* aBuf, size_t aCount, size_t aOffset) -{ - RecordReplayFunction(pread, ssize_t, aFd, aBuf, aCount, aOffset); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aCount); - MOZ_RELEASE_ASSERT(!aBuf || (size_t) rval <= aCount); - events.RecordOrReplayBytes(aBuf, rval); - return rval; -} - -static ssize_t -RR_write(int aFd, void* aBuf, size_t aCount) -{ - RecordReplayFunction(write, ssize_t, aFd, aBuf, aCount); - RecordOrReplayHadErrorNegative(rrf); - return rval; -} - -RRFunctionNegError3(__write_nocancel) -RRFunctionNegError3(open) -RRFunctionNegError3(__open_nocancel) -RRFunctionNegError3(shm_open) -RRFunctionNegError3(socket) -RRFunctionNegError0(kqueue) -RRFunctionNegError1(close) -RRFunctionNegError1(__close_nocancel) -RRFunctionNegError2(mkdir) -RRFunctionNegError1(dup) -RRFunctionNegError2(access) -RRFunctionNegError3(lseek) - -static ssize_t -RR_pipe(int* aFds) -{ - RecordReplayFunction(pipe, ssize_t, aFds); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayBytes(aFds, 2 * sizeof(int)); - return rval; -} - -static ssize_t -RR_socketpair(int aDomain, int aType, int aProtocol, int* aFds) -{ - RecordReplayFunction(socketpair, ssize_t, aDomain, aType, aProtocol, aFds); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayBytes(aFds, 2 * sizeof(int)); - return rval; -} - -static ssize_t -RR_fileport_makeport(int aFd, size_t* aPortName) -{ - RecordReplayFunction(fileport_makeport, ssize_t, aFd, aPortName); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayValue(aPortName); - return rval; -} - -static ssize_t -RR_getsockopt(int aSockFd, int aLevel, int aOptName, void* aOptVal, int* aOptLen) -{ - int initoptlen = *aOptLen; - RecordReplayFunction(getsockopt, ssize_t, aSockFd, aLevel, aOptName, aOptVal, aOptLen); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput((size_t) initoptlen); - events.RecordOrReplayValue(aOptLen); - MOZ_RELEASE_ASSERT(*aOptLen <= initoptlen); - events.RecordOrReplayBytes(aOptVal, *aOptLen); - return rval; -} - -static ssize_t -RR_gettimeofday(struct timeval* aTimeVal, struct timezone* aTimeZone) +static PreambleResult +Preamble_gettimeofday(CallArguments* aArguments) { // If we have diverged from the recording, just get the actual current time // rather than causing the current debugger operation to fail. This function // is frequently called via e.g. JS natives which the debugger will execute. if (HasDivergedFromRecording()) { - AutoEnsurePassThroughThreadEvents pt; - return OriginalCall(gettimeofday, ssize_t, aTimeVal, aTimeZone); + return PreambleResult::PassThrough; } - - RecordReplayFunction(gettimeofday, ssize_t, aTimeVal, aTimeZone); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput((aTimeVal ? 1 : 0) | (aTimeZone ? 2 : 0)); - if (aTimeVal) { - events.RecordOrReplayBytes(aTimeVal, sizeof(struct timeval)); - } - if (aTimeZone) { - events.RecordOrReplayBytes(aTimeZone, sizeof(struct timezone)); - } - return rval; + return PreambleResult::Redirect; } -RRFunction0(getuid) -RRFunction0(geteuid) -RRFunction0(getgid) -RRFunction0(getegid) -RRFunction0(issetugid) -RRFunction0(__gettid) -RRFunction0(getpid) - -static ssize_t -RR_fcntl(int aFd, int aCmd, size_t aArg) +static PreambleResult +Preamble_fcntl(CallArguments* aArguments) { - switch (aCmd) { + // Make sure fcntl is only used with a limited set of commands. + auto& cmd = aArguments->Arg<1, size_t>(); + switch (cmd) { case F_GETFL: case F_SETFL: case F_GETFD: @@ -833,181 +722,11 @@ RR_fcntl(int aFd, int aCmd, size_t aArg) default: MOZ_CRASH(); } - - RecordReplayFunction(fcntl, ssize_t, aFd, aCmd, aArg); - RecordOrReplayHadErrorNegative(rrf); - return rval; + return PreambleResult::Redirect; } -static ssize_t -RR_getattrlist(const char* aPath, struct attrlist* aAttrList, - void* aAttrBuf, size_t aAttrBufSize, unsigned long aOptions) -{ - RecordReplayFunction(getattrlist, ssize_t, aPath, aAttrList, aAttrBuf, aAttrBufSize, aOptions); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aAttrBufSize); - events.RecordOrReplayBytes(aAttrBuf, aAttrBufSize); - return rval; -} - -#define RecordReplayStatFunction(aName, aBufType) \ - static ssize_t \ - RR_ ##aName(size_t a0, aBufType* aBuf) \ - { \ - RecordReplayFunction(aName, ssize_t, a0, aBuf); \ - if (RecordOrReplayHadErrorNegative(rrf)) { \ - return rval; \ - } \ - events.RecordOrReplayBytes(aBuf, sizeof(aBufType)); \ - return rval; \ - } - -RecordReplayStatFunction(fstat$INODE64, struct stat) -RecordReplayStatFunction(lstat$INODE64, struct stat) -RecordReplayStatFunction(stat$INODE64, struct stat) -RecordReplayStatFunction(statfs$INODE64, struct statfs) -RecordReplayStatFunction(fstatfs$INODE64, struct statfs) - -static ssize_t -RR_readlink(const char* aPath, char* aBuf, size_t aBufSize) -{ - RecordReplayFunction(readlink, ssize_t, aPath, aBuf, aBufSize); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aBufSize); - events.RecordOrReplayBytes(aBuf, aBufSize); - return rval; -} - -static ssize_t -RR___getdirentries64(int aFd, void* aBuf, size_t aBufSize, size_t* aPosition) -{ - RecordReplayFunction(__getdirentries64, ssize_t, aFd, aBuf, aBufSize, aPosition); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aBufSize); - events.RecordOrReplayBytes(aBuf, aBufSize); - events.RecordOrReplayValue(aPosition); - return rval; -} - -static ssize_t -RR_getdirentriesattr(int aFd, struct attrlist* aAttrList, - void* aBuffer, size_t aBuffersize, - size_t* aCount, size_t* aBase, size_t* aNewState, size_t aOptions) -{ - RecordReplayFunction(getdirentriesattr, ssize_t, - aFd, aAttrList, aBuffer, aBuffersize, aCount, aBase, aNewState, aOptions); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aBuffersize); - events.RecordOrReplayBytes(aAttrList, sizeof(struct attrlist)); - events.RecordOrReplayBytes(aBuffer, aBuffersize); - events.RecordOrReplayValue(aCount); - events.RecordOrReplayValue(aBase); - events.RecordOrReplayValue(aNewState); - return rval; -} - -static ssize_t -RR_getrusage(int aWho, struct rusage* aUsage) -{ - RecordReplayFunction(getrusage, ssize_t, aWho, aUsage); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayBytes(aUsage, sizeof(*aUsage)); - return rval; -} - -static ssize_t -RR___getrlimit(int aWho, struct rlimit* aLimit) -{ - RecordReplayFunction(__getrlimit, ssize_t, aWho, aLimit); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayBytes(aLimit, sizeof(*aLimit)); - return rval; -} - -RRFunctionNegError2(__setrlimit) - -static ssize_t -RR_sigprocmask(int aHow, const sigset_t* aSet, sigset_t* aOldSet) -{ - RecordReplayFunction(sigprocmask, ssize_t, aHow, aSet, aOldSet); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(!!aOldSet); - if (aOldSet) { - events.RecordOrReplayBytes(aOldSet, sizeof(*aOldSet)); - } - return rval; -} - -static ssize_t -RR_sigaltstack(const stack_t* aSs, stack_t* aOldSs) -{ - RecordReplayFunction(sigaltstack, ssize_t, aSs, aOldSs); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(!!aOldSs); - if (aOldSs) { - events.RecordOrReplayBytes(aOldSs, sizeof(*aOldSs)); - } - return rval; -} - -static ssize_t -RR_sigaction(int aSignum, const struct sigaction* aAct, struct sigaction* aOldAct) -{ - RecordReplayFunction(sigaction, ssize_t, aSignum, aAct, aOldAct); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(!!aOldAct); - if (aOldAct) { - events.RecordOrReplayBytes(aOldAct, sizeof(struct sigaction)); - } - return rval; -} - -static ssize_t -RR___pthread_sigmask(int aHow, const sigset_t* aSet, sigset_t* aOldSet) -{ - RecordReplayFunction(__pthread_sigmask, ssize_t, aHow, aSet, aOldSet); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(!!aOldSet); - if (aOldSet) { - events.RecordOrReplayBytes(aOldSet, sizeof(sigset_t)); - } - return rval; -} - -static ssize_t -RR___fsgetpath(void* aBuf, size_t aBufSize, void* aFsId, uint64_t aObjId) -{ - RecordReplayFunction(__fsgetpath, ssize_t, aBuf, aBufSize, aFsId, aObjId); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aBufSize); - events.RecordOrReplayBytes(aBuf, aBufSize); - return rval; -} - -static ssize_t -RR___disable_threadsignal(int a0) +static PreambleResult +Preamble___disable_threadsignal(CallArguments* aArguments) { // __disable_threadsignal is called when a thread finishes. During replay a // terminated thread can cause problems such as changing access bits on @@ -1021,103 +740,26 @@ RR___disable_threadsignal(int a0) if (IsReplaying()) { Thread::WaitForeverNoIdle(); } - return OriginalCall(__disable_threadsignal, ssize_t, a0); + return PreambleResult::PassThrough; } -static ssize_t -RR___sysctl(int* aName, size_t aNamelen, void* aOld, size_t* aOldlenp, - void* aNewData, size_t aNewLen) +static void +RR___sysctl(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - size_t initlen = aOldlenp ? *aOldlenp : 0; - RecordReplayFunction(__sysctl, ssize_t, aName, aNamelen, aOld, aOldlenp, aNewData, aNewLen); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; + auto& old = aArguments->Arg<2, void*>(); + auto& oldlenp = aArguments->Arg<3, size_t*>(); + + aEvents.CheckInput((old ? 1 : 0) | (oldlenp ? 2 : 0)); + if (oldlenp) { + aEvents.RecordOrReplayValue(oldlenp); } - events.CheckInput((aOld ? 1 : 0) | (aOldlenp ? 2 : 0)); - events.CheckInput(initlen); - if (aOldlenp) { - events.RecordOrReplayValue(aOldlenp); + if (old) { + aEvents.RecordOrReplayBytes(old, *oldlenp); } - if (aOld) { - MOZ_RELEASE_ASSERT(aOldlenp); - events.RecordOrReplayBytes(aOld, *aOldlenp); - } - return rval; } -RRFunctionNegError3(__mac_syscall) - -static ssize_t -RR_getaudit_addr(auditinfo_addr_t* aAuditInfo, size_t aLength) -{ - RecordReplayFunction(getaudit_addr, ssize_t, aAuditInfo, aLength); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayBytes(aAuditInfo, sizeof(*aAuditInfo)); - return rval; -} - -RRFunction1(umask) - -static ssize_t -RR___select(int aFdCount, fd_set* aReadFds, fd_set* aWriteFds, - fd_set* aExceptFds, timeval* aTimeout) -{ - RecordReplayFunction(__select, ssize_t, aFdCount, aReadFds, aWriteFds, aExceptFds, aTimeout); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayBytes(aReadFds, sizeof(fd_set)); - events.RecordOrReplayBytes(aWriteFds, sizeof(fd_set)); - events.RecordOrReplayBytes(aExceptFds, sizeof(fd_set)); - events.CheckInput(!!aTimeout); - if (aTimeout) { - events.RecordOrReplayBytes(aTimeout, sizeof(timeval)); - } - return rval; -} - -RRFunctionNegError3(__process_policy); -RRFunctionNegError6(__kdebug_trace); - -static ssize_t -RR_guarded_kqueue_np(size_t* aGuard, int aFlags) -{ - RecordReplayFunction(guarded_kqueue_np, ssize_t, aGuard, aFlags); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.RecordOrReplayValue(aGuard); - return rval; -} - -static ssize_t -RR_csops(pid_t aPid, uint32_t aOps, void* aBuf, size_t aBufSize) -{ - RecordReplayFunction(csops, ssize_t, aPid, aOps, aBuf, aBufSize); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aBufSize); - events.RecordOrReplayBytes(aBuf, aBufSize); - return rval; -} - -static ssize_t -RR___getlogin(char* aName, size_t aLength) -{ - RecordReplayFunction(__getlogin, ssize_t, aName, aLength); - if (RecordOrReplayHadErrorNegative(rrf)) { - return rval; - } - events.CheckInput(aLength); - events.RecordOrReplayBytes(aName, aLength); - return rval; -} - -static ssize_t -RR___workq_kernreturn(size_t a0, size_t a1, size_t a2, size_t a3) +static PreambleResult +Preamble___workq_kernreturn(CallArguments* aArguments) { // Busy-wait until initialization is complete. while (!gInitialized) { @@ -1127,11 +769,12 @@ RR___workq_kernreturn(size_t a0, size_t a1, size_t a2, size_t a3) // Make sure we know this thread exists. Thread::Current(); - return OriginalCall(__workq_kernreturn, ssize_t, a0, a1, a2, a3); + RecordReplayInvokeCall(CallEvent___workq_kernreturn, aArguments); + return PreambleResult::Veto; } -static void -RR_start_wqthread(size_t a0, size_t a1, size_t a2, size_t a3, size_t a4, size_t a5, size_t a6, size_t a7) +static PreambleResult +Preamble_start_wqthread(CallArguments* aArguments) { // When replaying we don't want system threads to run, but by the time we // initialize the record/replay system GCD has already started running. @@ -1141,7 +784,8 @@ RR_start_wqthread(size_t a0, size_t a1, size_t a2, size_t a3, size_t a4, size_t Thread::WaitForeverNoIdle(); } - OriginalCall(start_wqthread, void, a0, a1, a2, a3, a4, a5, a6, a7); + RecordReplayInvokeCall(CallEvent_start_wqthread, aArguments); + return PreambleResult::Veto; } /////////////////////////////////////////////////////////////////////////////// @@ -1194,398 +838,280 @@ WaitForCvar(pthread_mutex_t* aMutex, bool aRecordReturnValue, return 0; } -static ssize_t -RR_pthread_cond_wait(pthread_cond_t* aCond, pthread_mutex_t* aMutex) +static PreambleResult +Preamble_pthread_cond_wait(CallArguments* aArguments) { - return WaitForCvar(aMutex, false, - [=]() { return OriginalCall(pthread_cond_wait, ssize_t, aCond, aMutex); }); + auto& cond = aArguments->Arg<0, pthread_cond_t*>(); + auto& mutex = aArguments->Arg<1, pthread_mutex_t*>(); + aArguments->Rval() = + WaitForCvar(mutex, false, + [=]() { return OriginalCall(pthread_cond_wait, ssize_t, cond, mutex); }); + return PreambleResult::Veto; } -static ssize_t -RR_pthread_cond_timedwait(pthread_cond_t* aCond, pthread_mutex_t* aMutex, - timespec* aTimeout) +static PreambleResult +Preamble_pthread_cond_timedwait(CallArguments* aArguments) { - return WaitForCvar(aMutex, true, - [=]() { return OriginalCall(pthread_cond_timedwait, ssize_t, - aCond, aMutex, aTimeout); }); + auto& cond = aArguments->Arg<0, pthread_cond_t*>(); + auto& mutex = aArguments->Arg<1, pthread_mutex_t*>(); + auto& timeout = aArguments->Arg<2, timespec*>(); + aArguments->Rval() = + WaitForCvar(mutex, true, + [=]() { return OriginalCall(pthread_cond_timedwait, ssize_t, + cond, mutex, timeout); }); + return PreambleResult::Veto; } -static ssize_t -RR_pthread_cond_timedwait_relative_np(pthread_cond_t* aCond, pthread_mutex_t* aMutex, - timespec* aTimeout) +static PreambleResult +Preamble_pthread_cond_timedwait_relative_np(CallArguments* aArguments) { - return WaitForCvar(aMutex, true, - [=]() { return OriginalCall(pthread_cond_timedwait_relative_np, ssize_t, - aCond, aMutex, aTimeout); }); + auto& cond = aArguments->Arg<0, pthread_cond_t*>(); + auto& mutex = aArguments->Arg<1, pthread_mutex_t*>(); + auto& timeout = aArguments->Arg<2, timespec*>(); + aArguments->Rval() = + WaitForCvar(mutex, true, + [=]() { return OriginalCall(pthread_cond_timedwait_relative_np, ssize_t, + cond, mutex, timeout); }); + return PreambleResult::Veto; } -static ssize_t -RR_pthread_create(pthread_t* aToken, const pthread_attr_t* aAttr, - void* (*aStart)(void*), void* aStartArg) +static PreambleResult +Preamble_pthread_create(CallArguments* aArguments) { if (AreThreadEventsPassedThrough()) { - return OriginalCall(pthread_create, ssize_t, aToken, aAttr, aStart, aStartArg); + return PreambleResult::Redirect; } + auto& token = aArguments->Arg<0, pthread_t*>(); + auto& attr = aArguments->Arg<1, const pthread_attr_t*>(); + auto& start = aArguments->Arg<2, void*>(); + auto& startArg = aArguments->Arg<3, void*>(); + int detachState; - int rv = pthread_attr_getdetachstate(aAttr, &detachState); + int rv = pthread_attr_getdetachstate(attr, &detachState); MOZ_RELEASE_ASSERT(rv == 0); - *aToken = Thread::StartThread((Thread::Callback) aStart, aStartArg, - detachState == PTHREAD_CREATE_JOINABLE); - return 0; + *token = Thread::StartThread((Thread::Callback) start, startArg, + detachState == PTHREAD_CREATE_JOINABLE); + aArguments->Rval() = 0; + return PreambleResult::Veto; } -static ssize_t -RR_pthread_join(pthread_t aToken, void** aPtr) +static PreambleResult +Preamble_pthread_join(CallArguments* aArguments) { if (AreThreadEventsPassedThrough()) { - return OriginalCall(pthread_join, int, aToken, aPtr); + return PreambleResult::Redirect; } - Thread* thread = Thread::GetByNativeId(aToken); + auto& token = aArguments->Arg<0, pthread_t>(); + auto& ptr = aArguments->Arg<1, void**>(); + + Thread* thread = Thread::GetByNativeId(token); thread->Join(); - *aPtr = nullptr; - return 0; + *ptr = nullptr; + aArguments->Rval() = 0; + return PreambleResult::Veto; } -static ssize_t -RR_pthread_mutex_init(pthread_mutex_t* aMutex, const pthread_mutexattr_t* aAttr) +static PreambleResult +Preamble_pthread_mutex_init(CallArguments* aArguments) { - Lock::New(aMutex); - return OriginalCall(pthread_mutex_init, ssize_t, aMutex, aAttr); + auto& mutex = aArguments->Arg<0, pthread_mutex_t*>(); + auto& attr = aArguments->Arg<1, pthread_mutexattr_t*>(); + + Lock::New(mutex); + aArguments->Rval() = OriginalCall(pthread_mutex_init, ssize_t, mutex, attr); + return PreambleResult::Veto; } -static ssize_t -RR_pthread_mutex_destroy(pthread_mutex_t* aMutex) +static PreambleResult +Preamble_pthread_mutex_destroy(CallArguments* aArguments) { - Lock::Destroy(aMutex); - return OriginalCall(pthread_mutex_destroy, ssize_t, aMutex); + auto& mutex = aArguments->Arg<0, pthread_mutex_t*>(); + + Lock::Destroy(mutex); + aArguments->Rval() = OriginalCall(pthread_mutex_destroy, ssize_t, mutex); + return PreambleResult::Veto; } -static ssize_t -RR_pthread_mutex_lock(pthread_mutex_t* aMutex) +static PreambleResult +Preamble_pthread_mutex_lock(CallArguments* aArguments) { - Lock* lock = Lock::Find(aMutex); + auto& mutex = aArguments->Arg<0, pthread_mutex_t*>(); + + Lock* lock = Lock::Find(mutex); if (!lock) { AutoEnsurePassThroughThreadEventsUseStackPointer pt; - return OriginalCall(pthread_mutex_lock, ssize_t, aMutex); + aArguments->Rval() = OriginalCall(pthread_mutex_lock, ssize_t, mutex); + return PreambleResult::Veto; } ssize_t rv = 0; if (IsRecording()) { AutoPassThroughThreadEvents pt; - rv = OriginalCall(pthread_mutex_lock, ssize_t, aMutex); + rv = OriginalCall(pthread_mutex_lock, ssize_t, mutex); } rv = RecordReplayValue(rv); MOZ_RELEASE_ASSERT(rv == 0 || rv == EDEADLK); if (rv == 0) { lock->Enter(); if (IsReplaying()) { - DirectLockMutex(aMutex); + DirectLockMutex(mutex); } } - return rv; + aArguments->Rval() = rv; + return PreambleResult::Veto; } -static ssize_t -RR_pthread_mutex_trylock(pthread_mutex_t* aMutex) +static PreambleResult +Preamble_pthread_mutex_trylock(CallArguments* aArguments) { - Lock* lock = Lock::Find(aMutex); + auto& mutex = aArguments->Arg<0, pthread_mutex_t*>(); + + Lock* lock = Lock::Find(mutex); if (!lock) { AutoEnsurePassThroughThreadEvents pt; - return OriginalCall(pthread_mutex_trylock, ssize_t, aMutex); + aArguments->Rval() = OriginalCall(pthread_mutex_trylock, ssize_t, mutex); + return PreambleResult::Veto; } ssize_t rv = 0; if (IsRecording()) { AutoPassThroughThreadEvents pt; - rv = OriginalCall(pthread_mutex_trylock, ssize_t, aMutex); + rv = OriginalCall(pthread_mutex_trylock, ssize_t, mutex); } rv = RecordReplayValue(rv); MOZ_RELEASE_ASSERT(rv == 0 || rv == EBUSY); if (rv == 0) { lock->Enter(); if (IsReplaying()) { - DirectLockMutex(aMutex); + DirectLockMutex(mutex); } } - return rv; + aArguments->Rval() = rv; + return PreambleResult::Veto; } -static ssize_t -RR_pthread_mutex_unlock(pthread_mutex_t* aMutex) +static PreambleResult +Preamble_pthread_mutex_unlock(CallArguments* aArguments) { - Lock* lock = Lock::Find(aMutex); + auto& mutex = aArguments->Arg<0, pthread_mutex_t*>(); + + Lock* lock = Lock::Find(mutex); if (!lock) { AutoEnsurePassThroughThreadEventsUseStackPointer pt; - return OriginalCall(pthread_mutex_unlock, ssize_t, aMutex); + aArguments->Rval() = OriginalCall(pthread_mutex_unlock, ssize_t, mutex); + return PreambleResult::Veto; } lock->Exit(); - DirectUnlockMutex(aMutex); - return 0; + DirectUnlockMutex(mutex); + aArguments->Rval() = 0; + return PreambleResult::Veto; } /////////////////////////////////////////////////////////////////////////////// // stdlib redirections /////////////////////////////////////////////////////////////////////////////// -static ssize_t -RR_dlclose(void* aHandle) -{ - return 0; -} - -static void* -RR_dlopen(const char* aFilename, int aFlags) -{ - AutoEnsurePassThroughThreadEvents pt; - return OriginalCall(dlopen, void*, aFilename, aFlags); -} - -static void* -RR_dlsym(void* aHandle, const char* aSymbol) -{ - AutoEnsurePassThroughThreadEvents pt; - return OriginalCall(dlsym, void*, aHandle, aSymbol); -} - -RRFunctionNegError1(fclose) -RRFunctionZeroError2(fopen) - -static size_t -RR_fread(void *aBuf, size_t aElemSize, size_t aCapacity, FILE *aFile) -{ - RecordReplayFunction(fread, size_t, aBuf, aElemSize, aCapacity, aFile); - events.RecordOrReplayValue(&rval); - events.CheckInput(aElemSize); - events.CheckInput(aCapacity); - MOZ_RELEASE_ASSERT((size_t)rval <= aCapacity); - events.RecordOrReplayBytes(aBuf, rval * aElemSize); - return rval; -} - -RRFunctionNegError3(fseek) -RRFunctionNegError1(ftell) -RRFunction4(fwrite) - static void -RecordReplayCString(Stream& aEvents, char** aString) +RR_fread(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - size_t len = (IsRecording() && *aString) ? strlen(*aString) + 1 : 0; - aEvents.RecordOrReplayValue(&len); - if (IsReplaying()) { - *aString = len ? NewLeakyArray(len) : nullptr; - } - if (len) { - aEvents.RecordOrReplayBytes(*aString, len); - } + auto& buf = aArguments->Arg<0, void*>(); + auto& elemSize = aArguments->Arg<1, size_t>(); + auto& capacity = aArguments->Arg<2, size_t>(); + auto& rval = aArguments->Rval(); + + aEvents.CheckInput(elemSize); + aEvents.CheckInput(capacity); + MOZ_RELEASE_ASSERT(rval <= capacity); + aEvents.RecordOrReplayBytes(buf, rval * elemSize); } -static char* -RR_getenv(char* aName) -{ - RecordReplayFunction(getenv, char*, aName); - RecordReplayCString(events, &rval); - return rval; -} - -#define RecordReplayTimeFunction(aName) \ - static struct tm* \ - RR_ ##aName(const time_t* aTime, struct tm* aResult) \ - { \ - MOZ_RELEASE_ASSERT(aResult); \ - RecordReplayFunction(aName, struct tm*, aTime, aResult); \ - if (RecordOrReplayHadErrorZero(rrf)) { \ - return nullptr; \ - } \ - events.RecordOrReplayBytes(aResult, sizeof(struct tm)); \ - if (IsRecording()) { \ - MOZ_RELEASE_ASSERT(rval == aResult); \ - } \ - return aResult; \ - } - -RecordReplayTimeFunction(localtime_r) -RecordReplayTimeFunction(gmtime_r) - static struct tm gGlobalTM; -// localtime and gmtime behave the same as localtime_r and gmtime_r, except -// they are not reentrant. -#define RecordReplayNonReentrantTimeFunction(aName) \ - static struct tm* \ - RR_ ##aName(const time_t* aTime) \ - { \ - return RR_ ##aName ##_r(aTime, &gGlobalTM); \ - } - -RecordReplayNonReentrantTimeFunction(localtime) -RecordReplayNonReentrantTimeFunction(gmtime) - -static time_t -RR_mktime(struct tm* aTime) +// localtime behaves the same as localtime_r, except it is not reentrant. +// For simplicity, define this in terms of localtime_r. +static PreambleResult +Preamble_localtime(CallArguments* aArguments) { - RecordReplayFunction(mktime, time_t, aTime); - events.RecordOrReplayBytes(aTime, sizeof(struct tm)); - events.RecordOrReplayValue(&rval); - return rval; + aArguments->Rval() = localtime_r(aArguments->Arg<0, const time_t*>(), &gGlobalTM); + return PreambleResult::Veto; } -static char* -RR_setlocale(int aCategory, const char* aLocale) +// The same concern here applies as for localtime. +static PreambleResult +Preamble_gmtime(CallArguments* aArguments) { - RecordReplayFunction(setlocale, char*, aCategory, aLocale); - RecordReplayCString(events, &rval); - return rval; + aArguments->Rval() = gmtime_r(aArguments->Arg<0, const time_t*>(), &gGlobalTM); + return PreambleResult::Veto; } -static size_t -RR_strftime(char* aStr, size_t aSize, const char* aFormat, const struct tm* aTime) -{ - RecordReplayFunction(strftime, size_t, aStr, aSize, aFormat, aTime); - events.CheckInput(aSize); - events.RecordOrReplayValue(&rval); - MOZ_RELEASE_ASSERT(rval < aSize); - events.RecordOrReplayBytes(aStr, rval + 1); - return rval; -} - -static size_t -RR_arc4random() -{ - RecordReplayFunction(arc4random, size_t); - events.RecordOrReplayValue(&rval); - return rval; -} - -static uint64_t -RR_mach_absolute_time() +static PreambleResult +Preamble_mach_absolute_time(CallArguments* aArguments) { // This function might be called through OSSpinLock while setting gTlsThreadKey. Thread* thread = Thread::GetByStackPointer(&thread); if (!thread || thread->PassThroughEvents()) { - return OriginalCall(mach_absolute_time, uint64_t); + aArguments->Rval() = OriginalCall(mach_absolute_time, uint64_t); + return PreambleResult::Veto; } - - RecordReplayFunction(mach_absolute_time, uint64_t); - events.RecordOrReplayValue(&rval); - return rval; + return PreambleResult::Redirect; } -static mach_msg_return_t -RR_mach_msg(mach_msg_header_t* aMsg, mach_msg_option_t aOption, mach_msg_size_t aSendSize, - mach_msg_size_t aReceiveLimit, mach_port_t aReceiveName, mach_msg_timeout_t aTimeout, - mach_port_t aNotify) +static PreambleResult +Preamble_mach_vm_allocate(CallArguments* aArguments) { - RecordReplayFunction(mach_msg, mach_msg_return_t, - aMsg, aOption, aSendSize, aReceiveLimit, aReceiveName, aTimeout, aNotify); - AutoOrderedAtomicAccess(); // mach_msg may be used for inter-thread synchronization. - events.RecordOrReplayValue(&rval); - events.CheckInput(aReceiveLimit); - if (aReceiveLimit) { - events.RecordOrReplayBytes(aMsg, aReceiveLimit); - } - return rval; + auto& address = aArguments->Arg<1, void**>(); + auto& size = aArguments->Arg<2, size_t>(); + *address = AllocateMemory(size, MemoryKind::Tracked); + aArguments->Rval() = KERN_SUCCESS; + return PreambleResult::Veto; } -static kern_return_t -RR_mach_timebase_info(mach_timebase_info_data_t* aInfo) +static PreambleResult +Preamble_mach_vm_deallocate(CallArguments* aArguments) { - RecordReplayFunction(mach_timebase_info, kern_return_t, aInfo); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aInfo, sizeof(*aInfo)); - return rval; + auto& address = aArguments->Arg<1, void*>(); + auto& size = aArguments->Arg<2, size_t>(); + DeallocateMemory(address, size, MemoryKind::Tracked); + aArguments->Rval() = KERN_SUCCESS; + return PreambleResult::Veto; } -static void -RR_sandbox_free_error(char* aBuf) -{ - if (IsRecording()) { - OriginalCall(sandbox_free_error, void, aBuf); - } else { - delete[] aBuf; - } -} - -static ssize_t -RR_sandbox_init(const char* aProfile, uint64_t aFlags, char** aErrorBuf) -{ - // By passing through events when initializing the sandbox, we ensure both - // that we actually initialize the process sandbox while replaying as well as - // while recording, and that any activity in here does not interfere with the - // replay. - AutoEnsurePassThroughThreadEvents pt; - return OriginalCall(sandbox_init, ssize_t, - aProfile, aFlags, aErrorBuf); -} - -static ssize_t -RR_sandbox_init_with_parameters(const char* aProfile, uint64_t aFlags, - const char *const aParameters[], char** aErrorBuf) -{ - // As for sandbox_init, call this function even while replaying. - AutoEnsurePassThroughThreadEvents pt; - return OriginalCall(sandbox_init_with_parameters, ssize_t, - aProfile, aFlags, aParameters, aErrorBuf); -} - -static kern_return_t -RR_task_threads(mach_port_t aTask, thread_act_array_t *aThreads, - mach_msg_type_number_t *aNumThreads) -{ - // Make sure events are passed through here so that replaying processes can - // inspect their own threads. - AutoEnsurePassThroughThreadEvents pt; - return OriginalCall(task_threads, kern_return_t, aTask, aThreads, aNumThreads); -} - -static kern_return_t -RR_mach_vm_allocate(vm_map_t aTarget, mach_vm_address_t* aAddress, - mach_vm_size_t aSize, int aFlags) -{ - *aAddress = (mach_vm_address_t) AllocateMemory(aSize, MemoryKind::Tracked); - return KERN_SUCCESS; -} - -static kern_return_t -RR_mach_vm_deallocate(vm_map_t aTarget, mach_vm_address_t aAddress, mach_vm_size_t aSize) -{ - DeallocateMemory((void*) aAddress, aSize, MemoryKind::Tracked); - return KERN_SUCCESS; -} - -static kern_return_t -RR_mach_vm_protect(vm_map_t aTarget, mach_vm_address_t aAddress, mach_vm_size_t aSize, - boolean_t aSetMaximum, vm_prot_t aNewProtection) +static PreambleResult +Preamble_mach_vm_protect(CallArguments* aArguments) { + // Ignore any mach_vm_protect calls that occur after saving a checkpoint, as for mprotect. if (!HasSavedCheckpoint()) { - return OriginalCall(mach_vm_protect, kern_return_t, - aTarget, aAddress, aSize, aSetMaximum, aNewProtection); + return PreambleResult::PassThrough; } - return KERN_SUCCESS; + aArguments->Rval() = KERN_SUCCESS; + return PreambleResult::Veto; } -static kern_return_t -RR_vm_purgable_control(vm_map_t aTarget, mach_vm_address_t aAddress, - vm_purgable_t aControl, int* aState) +static PreambleResult +Preamble_vm_purgable_control(CallArguments* aArguments) { // Never allow purging of volatile memory, to simplify memory snapshots. - *aState = VM_PURGABLE_NONVOLATILE; - return KERN_SUCCESS; + auto& state = aArguments->Arg<3, int*>(); + *state = VM_PURGABLE_NONVOLATILE; + aArguments->Rval() = KERN_SUCCESS; + return PreambleResult::Veto; } -static kern_return_t -RR_vm_copy(vm_map_t aTarget, vm_address_t aSourceAddress, - vm_size_t aSize, vm_address_t aDestAddress) +static PreambleResult +Preamble_vm_copy(CallArguments* aArguments) { // Asking the kernel to copy memory doesn't work right if the destination is // non-writable, so do the copy manually. - memcpy((void*) aDestAddress, (void*) aSourceAddress, aSize); - return KERN_SUCCESS; + auto& src = aArguments->Arg<1, void*>(); + auto& size = aArguments->Arg<2, size_t>(); + auto& dest = aArguments->Arg<3, void*>(); + memcpy(dest, src, size); + aArguments->Rval() = KERN_SUCCESS; + return PreambleResult::Veto; } -RRFunctionVoid0(tzset) - /////////////////////////////////////////////////////////////////////////////// // NSPR redirections /////////////////////////////////////////////////////////////////////////////// @@ -1593,114 +1119,43 @@ RRFunctionVoid0(tzset) // Even though NSPR is compiled as part of firefox, it is easier to just // redirect this stuff than get record/replay related changes into the source. -static PLHashTable* -RR_PL_NewHashTable(PRUint32 aNumBuckets, PLHashFunction aKeyHash, - PLHashComparator aKeyCompare, PLHashComparator aValueCompare, - const PLHashAllocOps *aAllocOps, void *aAllocPriv) +static PreambleResult +Preamble_PL_NewHashTable(CallArguments* aArguments) { - GeneratePLHashTableCallbacks(&aKeyHash, &aKeyCompare, &aValueCompare, &aAllocOps, &aAllocPriv); - return OriginalCall(PL_NewHashTable, PLHashTable*, - aNumBuckets, aKeyHash, aKeyCompare, aValueCompare, aAllocOps, aAllocPriv); + auto& keyHash = aArguments->Arg<1, PLHashFunction>(); + auto& keyCompare = aArguments->Arg<2, PLHashComparator>(); + auto& valueCompare = aArguments->Arg<3, PLHashComparator>(); + auto& allocOps = aArguments->Arg<4, const PLHashAllocOps*>(); + auto& allocPriv = aArguments->Arg<5, void*>(); + + GeneratePLHashTableCallbacks(&keyHash, &keyCompare, &valueCompare, &allocOps, &allocPriv); + return PreambleResult::PassThrough; } -static void -RR_PL_HashTableDestroy(PLHashTable* aTable) +static PreambleResult +Preamble_PL_HashTableDestroy(CallArguments* aArguments) { - void* priv = aTable->allocPriv; - OriginalCall(PL_HashTableDestroy, void, aTable); + auto& table = aArguments->Arg<0, PLHashTable*>(); + + void* priv = table->allocPriv; + OriginalCall(PL_HashTableDestroy, void, table); DestroyPLHashTableCallbacks(priv); + return PreambleResult::Veto; } /////////////////////////////////////////////////////////////////////////////// // Objective C redirections /////////////////////////////////////////////////////////////////////////////// -RRFunction2(class_getClassMethod) -RRFunction2(class_getInstanceMethod) -RRFunctionVoid2(method_exchangeImplementations) -RRFunctionVoid1(objc_autoreleasePoolPop) -RRFunction0(objc_autoreleasePoolPush) - -// Capture the arguments that can be passed to an Objective C message. We only -// need to capture enough argument data here for messages sent directly from -// Gecko code (i.e. where events are not passed through). Messages sent while -// events are passed through are performed with the same stack and register -// state as when they were initially invoked. -struct MessageArguments -{ - size_t obj; // 0 - const char* msg; // 8 - size_t arg0; // 16 - size_t arg1; // 24 - size_t arg2; // 32 - size_t arg3; // 40 - double floatarg0; // 48 - size_t scratch; // 56 - size_t stack[64]; // 64 -}; // 576 - -extern "C" { - -extern size_t -RecordReplayInvokeObjCMessage(MessageArguments* aArguments, void* aFnPtr); - -__asm( -"_RecordReplayInvokeObjCMessage:" - - // 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 _RecordReplayInvokeObjCMessage_Loop;" - - // Copy each stack argument to the stack. -"_RecordReplayInvokeObjCMessage_Loop:" - "subq $1, %rsi;" - "movq 64(%rdi, %rsi, 8), %rdx;" - "push %rdx;" - "testq %rsi, %rsi;" - "jne _RecordReplayInvokeObjCMessage_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;" - "movq 0(%rdi), %rdi;" - - // Call the saved function pointer. - "callq *%rax;" - - // Pop the copied stack arguments. - "addq $512, %rsp;" - - // Save any floating point rval to the arguments. - "pop %rdi;" - "movsd %xmm0, 48(%rdi);" - - "ret;" -); - -} // extern "C" - static bool -TestObjCObjectClass(size_t aObj, const char* aName) +TestObjCObjectClass(id aObj, const char* aName) { // When recording we can test to see what the class of an object is, but we // have to record the result of the test because the object "pointers" we // have when replaying are not valid. bool found = false; if (IsRecording()) { - Class cls = object_getClass((id)aObj); + Class cls = object_getClass(aObj); while (cls) { if (!strcmp(class_getName(cls), aName)) { found = true; @@ -1726,33 +1181,31 @@ struct NSFastEnumerationState // thrown by mutating the array while it is being iterated over. static unsigned long gNeverChange; -extern "C" { - -size_t __attribute__((used)) -RecordReplayInterceptObjCMessage(MessageArguments* aArguments) +static PreambleResult +Preamble_objc_msgSend(CallArguments* aArguments) { Thread* thread = Thread::Current(); if (!thread || thread->PassThroughEvents()) { - aArguments->scratch = (size_t) OriginalFunction(CallEvent_objc_msgSend); - return 1; + return PreambleResult::Redirect; } EnsureNotDivergedFromRecording(); - thread->Events().RecordOrReplayThreadEvent(CallIdToThreadEvent(CallEvent_objc_msgSend)); - thread->Events().CheckInput(aArguments->msg); + auto& object = aArguments->Arg<0, id>(); + auto& message = aArguments->Arg<1, const char*>(); + + thread->Events().RecordOrReplayThreadEvent(CallIdToThreadEvent(CallEvent_objc_msgSend)); + thread->Events().CheckInput(message); - size_t rval = 0; - double floatRval = 0; bool handled = false; // Watch for some top level NSApplication messages that can cause Gecko // events to be processed. - if ((!strcmp(aArguments->msg, "run") || - !strcmp(aArguments->msg, "nextEventMatchingMask:untilDate:inMode:dequeue:")) && - TestObjCObjectClass(aArguments->obj, "NSApplication")) + if ((!strcmp(message, "run") || + !strcmp(message, "nextEventMatchingMask:untilDate:inMode:dequeue:")) && + TestObjCObjectClass(object, "NSApplication")) { PassThroughThreadEventsAllowCallbacks([&]() { - rval = RecordReplayInvokeObjCMessage(aArguments, OriginalFunction(CallEvent_objc_msgSend)); + RecordReplayInvokeCall(CallEvent_objc_msgSend, aArguments); }); handled = true; } @@ -1760,19 +1213,19 @@ RecordReplayInterceptObjCMessage(MessageArguments* aArguments) // Other messages are performed as normal. if (!handled && IsRecording()) { AutoPassThroughThreadEvents pt; - rval = RecordReplayInvokeObjCMessage(aArguments, OriginalFunction(CallEvent_objc_msgSend)); - floatRval = aArguments->floatarg0; + RecordReplayInvokeCall(CallEvent_objc_msgSend, aArguments); } - rval = RecordReplayValue(rval); - RecordReplayBytes(&floatRval, sizeof(double)); + RecordReplayBytes(&aArguments->Rval(), sizeof(size_t)); + RecordReplayBytes(&aArguments->FloatRval(), sizeof(double)); // Do some extra recording on messages that return additional state. - if (!strcmp(aArguments->msg, "countByEnumeratingWithState:objects:count:") && - TestObjCObjectClass(aArguments->obj, "NSArray")) + if (!strcmp(message, "countByEnumeratingWithState:objects:count:") && + TestObjCObjectClass(object, "NSArray")) { - NSFastEnumerationState* state = (NSFastEnumerationState*)aArguments->arg0; + auto& state = aArguments->Arg<2, NSFastEnumerationState*>(); + auto& rval = aArguments->Rval(); if (IsReplaying()) { state->itemsPtr = NewLeakyArray(rval); state->mutationsPtr = &gNeverChange; @@ -1780,196 +1233,53 @@ RecordReplayInterceptObjCMessage(MessageArguments* aArguments) RecordReplayBytes(state->itemsPtr, rval * sizeof(id)); } - if (!strcmp(aArguments->msg, "getCharacters:") && - TestObjCObjectClass(aArguments->obj, "NSString")) + if (!strcmp(message, "getCharacters:") && + TestObjCObjectClass(object, "NSString")) { size_t len = 0; if (IsRecording()) { AutoPassThroughThreadEvents pt; - len = CFStringGetLength((CFStringRef) aArguments->obj); + len = CFStringGetLength((CFStringRef) object); } len = RecordReplayValue(len); - RecordReplayBytes((void*)aArguments->arg0, len * sizeof(wchar_t)); + RecordReplayBytes(aArguments->Arg<2, void*>(), len * sizeof(wchar_t)); } - if ((!strcmp(aArguments->msg, "UTF8String") || - !strcmp(aArguments->msg, "cStringUsingEncoding:")) && - TestObjCObjectClass(aArguments->obj, "NSString")) + if ((!strcmp(message, "UTF8String") || + !strcmp(message, "cStringUsingEncoding:")) && + TestObjCObjectClass(object, "NSString")) { - size_t len = RecordReplayValue(IsRecording() ? strlen((const char*) rval) : 0); + auto& rval = aArguments->Rval(); + size_t len = RecordReplayValue(IsRecording() ? strlen(rval) : 0); if (IsReplaying()) { - rval = (size_t) NewLeakyArray(len + 1); + rval = NewLeakyArray(len + 1); } - RecordReplayBytes((void*) rval, len + 1); + RecordReplayBytes(rval, len + 1); } - // Remember the return value for the caller. - aArguments->scratch = rval; - aArguments->floatarg0 = floatRval; - return 0; + return PreambleResult::Veto; } -extern size_t -RR_objc_msgSend(id, SEL, ...); - -__asm( -"_RR_objc_msgSend:" - - // Make space for a MessageArguments struct on the stack. - "subq $576, %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);" - - // 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 _RR_objc_msgSend_Loop;" - - // Save stack arguments into the structure. -"_RR_objc_msgSend_Loop:" - "subq $1, %rsi;" - "movq 584(%rsp, %rsi, 8), %rdx;" // Ignore the return ip on the stack. - "movq %rdx, 64(%rsp, %rsi, 8);" - "testq %rsi, %rsi;" - "jne _RR_objc_msgSend_Loop;" - - // Place the structure's address into the first argument register. - "movq %rsp, %rdi;" - - // The stack must be have 16 byte alignment. The MessageArguments structure - // uses 576 bytes, and the frame pointer pushed by the callee uses 8 bytes, - // so push 8 bytes of empty space. - "subq $8, %rsp;" - "call _RecordReplayInterceptObjCMessage;" - "addq $8, %rsp;" - - "testq %rax, %rax;" - "je RR_objc_msgSend_done;" - - // Events are passed through. Invoke the message 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;" - "movq 56(%rsp), %rax;" - "addq $576, %rsp;" - "jmpq *%rax;" - - // The message has been recorded/replayed. -"RR_objc_msgSend_done:" - // Restore scalar and floating point return values. - "movsd 48(%rsp), %xmm0;" - "movq 56(%rsp), %rax;" - - // Pop the structure from the stack. - "addq $576, %rsp;" - - // Return to caller. - "ret;" -); - -} // extern "C" - /////////////////////////////////////////////////////////////////////////////// // Cocoa redirections /////////////////////////////////////////////////////////////////////////////// -RRFunction4(AcquireFirstMatchingEventInQueue) - static void -RR_CFArrayAppendValue(CFArrayRef aArray, CFTypeRef aValue) +RR_CFDataGetBytePtr(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - RecordReplayFunctionVoid(CFArrayAppendValue, aArray, aValue); -} + auto& rval = aArguments->Rval(); -RRFunction4(CFArrayCreate) -RRFunction1(CFArrayGetCount) -RRFunction2(CFArrayGetValueAtIndex) -RRFunctionVoid2(CFArrayRemoveValueAtIndex) -RRFunction3(CFAttributedStringCreate) -RRFunction1(CFBundleCopyExecutableURL) -RRFunction1(CFBundleCopyInfoDictionaryForURL) -RRFunction2(CFBundleCreate) -RRFunction1(CFBundleGetBundleWithIdentifier) - -static void* -RR_CFBundleGetDataPointerForName(CFBundleRef aBundle, CFStringRef aSymbolName) -{ - if (AreThreadEventsPassedThrough()) { - return OriginalCall(CFBundleGetDataPointerForName, void*, aBundle, aSymbolName); - } - return nullptr; -} - -static void* -RR_CFBundleGetFunctionPointerForName(CFBundleRef aBundle, CFStringRef aFunctionName) -{ - if (AreThreadEventsPassedThrough()) { - return OriginalCall(CFBundleGetFunctionPointerForName, void*, aBundle, aFunctionName); - } - return nullptr; -} - -RRFunction1(CFBundleGetIdentifier) -RRFunction2(CFBundleGetValueForInfoDictionaryKey) -RRFunction1(CFBundleGetInfoDictionary) -RRFunction0(CFBundleGetMainBundle) - -static const UInt8* -RR_CFDataGetBytePtr(CFDataRef aData) -{ - RecordReplayFunction(CFDataGetBytePtr, const UInt8*, aData); size_t len = 0; if (IsRecording()) { - len = OriginalCall(CFDataGetLength, size_t, aData); + len = OriginalCall(CFDataGetLength, size_t, aArguments->Arg<0, CFDataRef>()); } - events.RecordOrReplayValue(&len); + aEvents.RecordOrReplayValue(&len); if (IsReplaying()) { rval = NewLeakyArray(len); } - events.RecordOrReplayBytes((void*) rval, len); - return rval; + aEvents.RecordOrReplayBytes(rval, len); } -RRFunction1(CFDataGetLength) -RRFunction4(CFDateFormatterCreate) -RRFunction1(CFDateFormatterGetFormat) -RRFunctionVoid3(CFDictionaryAddValue) -RRFunction6(CFDictionaryCreate) -RRFunction4(CFDictionaryCreateMutable) -RRFunction3(CFDictionaryCreateMutableCopy) -RRFunction2(CFDictionaryGetValue) - -static Boolean -RR_CFDictionaryGetValueIfPresent(CFDictionaryRef aDict, const void *aKey, const void **aValue) -{ - RecordReplayFunction(CFDictionaryGetValueIfPresent, Boolean, aDict, aKey, aValue); - events.RecordOrReplayValue(aValue); - events.RecordOrReplayValue(&rval); - return rval; -} - -RRFunctionVoid3(CFDictionaryReplaceValue) -RRFunction2(CFEqual) -RRFunction1(CFGetTypeID) -RRFunction0(CFLocaleCopyCurrent) -RRFunction0(CFLocaleCopyPreferredLanguages) -RRFunction2(CFLocaleCreate) -RRFunction1(CFLocaleGetIdentifier) - static void DummyCFNotificationCallback(CFNotificationCenterRef aCenter, void* aObserver, CFStringRef aName, const void* aObject, CFDictionaryRef aUserInfo) @@ -1978,24 +1288,16 @@ static void DummyCFNotificationCallback(CFNotificationCenterRef aCenter, void* a //MOZ_CRASH(); } -static void -RR_CFNotificationCenterAddObserver(CFNotificationCenterRef aCenter, const void* aObserver, - CFNotificationCallback aCallback, CFStringRef aName, - const void* aObject, - CFNotificationSuspensionBehavior aSuspensionBehavior) +static PreambleResult +Preamble_CFNotificationCenterAddObserver(CallArguments* aArguments) { + auto& callback = aArguments->Arg<2, CFNotificationCallback>(); if (!AreThreadEventsPassedThrough()) { - aCallback = DummyCFNotificationCallback; + callback = DummyCFNotificationCallback; } - - RecordReplayFunctionVoid(CFNotificationCenterAddObserver, - aCenter, aObserver, aCallback, aName, aObject, aSuspensionBehavior); + return PreambleResult::Redirect; } -RRFunction0(CFNotificationCenterGetLocalCenter) -RRFunctionVoid4(CFNotificationCenterRemoveObserver) -RRFunction3(CFNumberCreate) - static size_t CFNumberTypeBytes(CFNumberType aType) { @@ -2020,153 +1322,34 @@ CFNumberTypeBytes(CFNumberType aType) } } -static Boolean -RR_CFNumberGetValue(CFNumberRef aNumber, CFNumberType aType, void* aValuePtr) -{ - RecordReplayFunction(CFNumberGetValue, Boolean, aNumber, aType, aValuePtr); - events.CheckInput(aType); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aValuePtr, CFNumberTypeBytes(aType)); - return rval; -} - -RRFunction1(CFNumberIsFloatType) -RRFunction2(CFPreferencesAppValueIsForced) -RRFunction2(CFPreferencesCopyAppValue) -RRFunction4(CFPreferencesCopyValue) -RRFunction4(CFPropertyListCreateFromXMLData) -RRFunction6(CFPropertyListCreateWithStream) -RRFunctionVoid1(CFReadStreamClose) -RRFunction2(CFReadStreamCreateWithFile) -RRFunction1(CFReadStreamOpen) -RRFunction1(CFRelease) -RRFunction1(CFRetain) -RRFunctionVoid3(CFRunLoopAddSource) -RRFunction0(CFRunLoopGetCurrent) -RRFunctionVoid3(CFRunLoopRemoveSource) - -static CFRunLoopSourceRef -RR_CFRunLoopSourceCreate(CFAllocatorRef aAllocator, CFIndex aOrder, CFRunLoopSourceContext* aCx) -{ - if (AreThreadEventsPassedThrough()) { - return OriginalCall(CFRunLoopSourceCreate, CFRunLoopSourceRef, aAllocator, aOrder, aCx); - } - - RegisterCallbackData(BitwiseCast(aCx->perform)); - RegisterCallbackData(aCx->info); - - if (IsRecording()) { - CallbackWrapperData* wrapperData = new CallbackWrapperData(aCx->perform, aCx->info); - aCx->perform = CFRunLoopPerformCallBackWrapper; - aCx->info = wrapperData; - } - - RecordReplayFunction(CFRunLoopSourceCreate, CFRunLoopSourceRef, aAllocator, aOrder, aCx); - events.RecordOrReplayValue((size_t*)&rval); - return rval; -} - -RRFunctionVoid1(CFRunLoopSourceSignal) -RRFunctionVoid1(CFRunLoopWakeUp) -RRFunctionVoid3(CFStringAppendCharacters) -RRFunction3(CFStringCompare) -RRFunction3(CFStringCreateArrayBySeparatingStrings) -RRFunction2(CFStringCreateMutable) -RRFunction5(CFStringCreateWithBytes) -RRFunction6(CFStringCreateWithBytesNoCopy) -RRFunction4(CFStringCreateWithCharactersNoCopy) -RRFunction3(CFStringCreateWithCString) -RRFunction10(CFStringCreateWithFormat) // Hope this is enough arguments... - -static CFIndex -RR_CFStringGetBytes(CFStringRef aString, CFRange aRange, CFStringEncoding aEncoding, - UInt8 aLossByte, Boolean aIsExternalRepresentation, - UInt8* aBuffer, CFIndex aMaxBufLen, CFIndex* aUsedBufLen) -{ - RecordReplayFunction(CFStringGetBytes, CFIndex, - aString, aRange, aEncoding, aLossByte, aIsExternalRepresentation, - aBuffer, aMaxBufLen, aUsedBufLen); - events.CheckInput(aMaxBufLen); - events.CheckInput((aUsedBufLen ? 1 : 0) | (aBuffer ? 2 : 0)); - events.RecordOrReplayValue(&rval); - if (aUsedBufLen) { - events.RecordOrReplayValue(aUsedBufLen); - } - if (aBuffer) { - events.RecordOrReplayBytes(aBuffer, aUsedBufLen ? *aUsedBufLen : aMaxBufLen); - } - return rval; -} - static void -RR_CFStringGetCharacters(CFStringRef aString, CFRange aRange, UniChar* aBuffer) +RR_CFNumberGetValue(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - RecordReplayFunctionVoid(CFStringGetCharacters, aString, aRange, aBuffer); - events.CheckInput(aRange.length); - events.RecordOrReplayBytes(aBuffer, aRange.length * sizeof(UniChar)); + auto& type = aArguments->Arg<1, CFNumberType>(); + auto& value = aArguments->Arg<2, void*>(); + + aEvents.CheckInput(type); + aEvents.RecordOrReplayBytes(value, CFNumberTypeBytes(type)); } -RRFunction1(CFStringGetIntValue) - -static Boolean -RR_CFStringGetCString(CFStringRef aString, char* aBuffer, CFIndex aBufferSize, - CFStringEncoding aEncoding) +static PreambleResult +Preamble_CFRunLoopSourceCreate(CallArguments* aArguments) { - RecordReplayFunction(CFStringGetCString, Boolean, - aString, aBuffer, aBufferSize, aEncoding); - events.CheckInput(aBufferSize); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aBuffer, aBufferSize); - return rval; -} + if (!AreThreadEventsPassedThrough()) { + auto& cx = aArguments->Arg<2, CFRunLoopSourceContext*>(); -static const char* -RR_CFStringGetCStringPtr(CFStringRef aString, CFStringEncoding aEncoding) -{ - if (AreThreadEventsPassedThrough()) { - return OriginalCall(CFStringGetCStringPtr, const char*, aString, aEncoding); + RegisterCallbackData(BitwiseCast(cx->perform)); + RegisterCallbackData(cx->info); + + if (IsRecording()) { + CallbackWrapperData* wrapperData = new CallbackWrapperData(cx->perform, cx->info); + cx->perform = CFRunLoopPerformCallBackWrapper; + cx->info = wrapperData; + } } - return nullptr; + return PreambleResult::Redirect; } -RRFunction1(CFStringGetLength) -RRFunction2(CFStringGetMaximumSizeForEncoding) -RRFunction2(CFStringHasPrefix) -RRFunction1(CFStringTokenizerAdvanceToNextToken) -RRFunction5(CFStringTokenizerCreate) -RRFunctionTypes1(CFStringTokenizerGetCurrentTokenRange, CFRange, CFStringTokenizerRef) -RRFunction4(CFURLCreateFromFileSystemRepresentation) -RRFunction2(CFURLCreateFromFSRef) -RRFunction3(CFURLCreateWithString) -RRFunction4(CFURLCreateWithFileSystemPath) - -static Boolean -RR_CFURLGetFileSystemRepresentation(CFURLRef aUrl, Boolean aResolveAgainstBase, - UInt8* aBuffer, CFIndex aMaxBufLen) -{ - RecordReplayFunction(CFURLGetFileSystemRepresentation, Boolean, - aUrl, aResolveAgainstBase, aBuffer, aMaxBufLen); - events.CheckInput(aMaxBufLen); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aBuffer, aMaxBufLen); - return rval; -} - -static Boolean -RR_CFURLGetFSRef(CFURLRef aUrl, struct FSRef* aFsRef) -{ - RecordReplayFunction(CFURLGetFSRef, Boolean, aUrl, aFsRef); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aFsRef, sizeof(FSRef)); - return rval; -} - -RRFunction1(CFUUIDCreate) -RRFunction2(CFUUIDCreateString) -RRFunctionTypes1(CFUUIDGetUUIDBytes, CFUUIDBytes, CFUUIDRef) -RRFunctionTypes2(CGAffineTransformConcat, CGAffineTransform, CGAffineTransform, CGAffineTransform) -RRFunction1(CGBitmapContextCreateImage) - struct ContextDataInfo { CGContextRef mContext; @@ -2180,28 +1363,26 @@ struct ContextDataInfo static StaticInfallibleVector gContextData; -static CGContextRef -RR_CGBitmapContextCreateWithData(void *aData, size_t aWidth, size_t aHeight, - size_t aBitsPerComponent, size_t aBytesPerRow, - CGColorSpaceRef aSpace, uint32_t aBitmapInfo, - CGBitmapContextReleaseDataCallback aReleaseCallback, - void *aReleaseInfo) +static void +RR_CGBitmapContextCreateWithData(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - RecordReplayFunction(CGBitmapContextCreateWithData, CGContextRef, - aData, aWidth, aHeight, aBitsPerComponent, aBytesPerRow, aSpace, - aBitmapInfo, aReleaseCallback, aReleaseInfo); - events.RecordOrReplayValue(&rval); + auto& data = aArguments->Arg<0, void*>(); + auto& height = aArguments->Arg<2, size_t>(); + auto& bytesPerRow = aArguments->Arg<4, size_t>(); + auto& rval = aArguments->Rval(); MOZ_RELEASE_ASSERT(Thread::CurrentIsMainThread()); - gContextData.emplaceBack(rval, aData, aHeight * aBytesPerRow); - return rval; + gContextData.emplaceBack(rval, data, height * bytesPerRow); } +template static void -FlushContext(CGContextRef aContext) +RR_FlushCGContext(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { + auto& context = aArguments->Arg(); + for (int i = gContextData.length() - 1; i >= 0; i--) { - if (aContext == gContextData[i].mContext) { + if (context == gContextData[i].mContext) { RecordReplayBytes(gContextData[i].mData, gContextData[i].mDataSize); return; } @@ -2209,519 +1390,99 @@ FlushContext(CGContextRef aContext) MOZ_CRASH(); } -RRFunction1(CGBitmapContextGetBytesPerRow) -RRFunction1(CGBitmapContextGetHeight) -RRFunction1(CGBitmapContextGetWidth) -RRFunction1(CGColorRelease) -RRFunction0(CGColorSpaceCreateDeviceRGB) -RRFunction1(CGColorSpaceCreatePattern) -RRFunction1(CGColorSpaceCopyICCProfile) -RRFunction1(CGColorSpaceRelease) -RRFunctionTypesVoid3(CGContextBeginTransparencyLayerWithRect, - CGContextRef, CGRect, CFDictionaryRef) -RRFunction3(CGContextClipToRects) -RRFunctionTypesVoid2(CGContextConcatCTM, CGContextRef, CGAffineTransform) - -static void -RR_CGContextDrawImage(CGContextRef aCx, CGRect aRect, CGImageRef aImage) +static PreambleResult +Preamble_CGContextRestoreGState(CallArguments* aArguments) { - RecordReplayFunctionVoid(CGContextDrawImage, aCx, aRect, aImage); - FlushContext(aCx); + return IsRecording() ? PreambleResult::PassThrough : PreambleResult::Veto; } -RRFunctionVoid1(CGContextEndTransparencyLayer) - static void -RR_CGContextFillRect(CGContextRef aCx, CGRect aRect) +RR_CGDataProviderCreateWithData(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - RecordReplayFunctionVoid(CGContextFillRect, aCx, aRect); - FlushContext(aCx); -} + auto& info = aArguments->Arg<0, void*>(); + auto& data = aArguments->Arg<1, const void*>(); + auto& size = aArguments->Arg<2, size_t>(); + auto& releaseData = aArguments->Arg<3, CGDataProviderReleaseDataCallback>(); -RRFunctionTypes1(CGContextGetClipBoundingBox, CGRect, CGContextRef) -RRFunctionTypes1(CGContextGetCTM, CGAffineTransform, CGContextRef) -RRFunction1(CGContextGetType) -RRFunctionTypes1(CGContextGetUserSpaceToDeviceSpaceTransform, CGAffineTransform, CGContextRef) - -static void -RR_CGContextRestoreGState(CGContextRef aCx) -{ - if (!IsReplaying()) { - Maybe pt; - if (!AreThreadEventsPassedThrough()) { - pt.emplace(); - } - OriginalCall(CGContextRestoreGState, void, aCx); - } -} - -RRFunctionVoid1(CGContextSaveGState) -RRFunctionVoid2(CGContextSetAllowsFontSubpixelPositioning) -RRFunctionVoid2(CGContextSetAllowsFontSubpixelQuantization) -RRFunctionTypesVoid2(CGContextSetAlpha, CGContextRef, CGFloat) -RRFunctionTypesVoid2(CGContextSetBaseCTM, CGContextRef, CGAffineTransform) -RRFunctionTypesVoid2(CGContextSetCTM, CGContextRef, CGAffineTransform) -RRFunctionTypesVoid3(CGContextSetGrayFillColor, CGContextRef, CGFloat, CGFloat) -RRFunctionTypesVoid5(CGContextSetRGBFillColor, CGContextRef, CGFloat, CGFloat, CGFloat, CGFloat) -RRFunctionVoid2(CGContextSetShouldAntialias) -RRFunctionVoid2(CGContextSetShouldSmoothFonts) -RRFunctionVoid2(CGContextSetShouldSubpixelPositionFonts) -RRFunctionVoid2(CGContextSetShouldSubpixelQuantizeFonts) -RRFunctionVoid2(CGContextSetTextDrawingMode) -RRFunctionTypesVoid2(CGContextSetTextMatrix, CGContextRef, CGAffineTransform) -RRFunctionVoid3(CGContextScaleCTM) -RRFunctionVoid3(CGContextTranslateCTM) - -static CGDataProviderRef -RR_CGDataProviderCreateWithData(void* aInfo, const void* aData, - size_t aSize, CGDataProviderReleaseDataCallback aReleaseData) -{ - RecordReplayFunction(CGDataProviderCreateWithData, CGDataProviderRef, - aInfo, aData, aSize, aReleaseData); - events.RecordOrReplayValue((size_t*)&rval); if (IsReplaying()) { // Immediately release the data, since there is no data provider to do it for us. - aReleaseData(aInfo, aData, aSize); + releaseData(info, data, size); } - return rval; } -RRFunction1(CGDisplayCopyColorSpace) -RRFunction1(CGDisplayIOServicePort) -RRFunction2(CGEventSourceCounterForEventType) -RRFunction2(CGFontCopyTableForTag) -RRFunction1(CGFontCopyTableTags) -RRFunction1(CGFontCopyVariations) -RRFunction2(CGFontCreateCopyWithVariations) -RRFunction1(CGFontCreateWithDataProvider) -RRFunction1(CGFontCreateWithFontName) -RRFunction1(CGFontCreateWithPlatformFont) -RRFunction1(CGFontGetAscent) -RRFunction1(CGFontGetCapHeight) -RRFunction1(CGFontGetDescent) -RRFunctionTypes1(CGFontGetFontBBox, CGRect, CGFontRef) - -static bool -RR_CGFontGetGlyphAdvances(CGFontRef aFont, const CGGlyph* aGlyphs, size_t aCount, int* aAdvances) -{ - RecordReplayFunction(CGFontGetGlyphAdvances, bool, aFont, aGlyphs, aCount, aAdvances); - events.CheckInput(aCount); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aAdvances, aCount * sizeof(int)); - return rval; -} - -static bool -RR_CGFontGetGlyphBBoxes(CGFontRef aFont, const CGGlyph* aGlyphs, size_t aCount, CGRect* aBoxes) -{ - RecordReplayFunction(CGFontGetGlyphBBoxes, bool, aFont, aGlyphs, aCount, aBoxes); - events.CheckInput(aCount); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aBoxes, aCount * sizeof(CGRect)); - return rval; -} - -RRFunction4(CGFontGetGlyphPath) -RRFunction1(CGFontGetLeading) -RRFunction1(CGFontGetUnitsPerEm) -RRFunction1(CGFontGetXHeight) -RRFunction1(CGImageGetHeight) -RRFunction1(CGImageGetWidth) -RRFunction1(CGImageRelease) -RRFunction0(CGMainDisplayID) -RRFunctionVoid3(CGPathAddPath) - -static void -RR_CGPathApply(CGPathRef aPath, void* aData, CGPathApplierFunction aFunction) +static PreambleResult +Preamble_CGPathApply(CallArguments* aArguments) { if (AreThreadEventsPassedThrough()) { - OriginalCall(CGPathApply, void, aPath, aData, aFunction); - } else { - RegisterCallbackData(BitwiseCast(aFunction)); - RegisterCallbackData(aData); - PassThroughThreadEventsAllowCallbacks([&]() { - CallbackWrapperData wrapperData(aFunction, aData); - OriginalCall(CGPathApply, void, aPath, &wrapperData, CGPathApplierFunctionWrapper); - }); - RemoveCallbackData(aData); + return PreambleResult::Redirect; } + + auto& path = aArguments->Arg<0, CGPathRef>(); + auto& data = aArguments->Arg<1, void*>(); + auto& function = aArguments->Arg<2, CGPathApplierFunction>(); + + RegisterCallbackData(BitwiseCast(function)); + RegisterCallbackData(data); + PassThroughThreadEventsAllowCallbacks([&]() { + CallbackWrapperData wrapperData(function, data); + OriginalCall(CGPathApply, void, path, &wrapperData, CGPathApplierFunctionWrapper); + }); + RemoveCallbackData(data); + + return PreambleResult::Veto; } -RRFunction4(CGPathContainsPoint) -RRFunction0(CGPathCreateMutable) -RRFunctionTypes1(CGPathGetBoundingBox, CGRect, CGPathRef) - -static CGRect -RR_CTFontGetBoundingRectsForGlyphs(CTFontRef aFont, CTFontOrientation aOrientation, - const CGGlyph* aGlyphs, CGRect* aRects, CFIndex aCount) -{ - RecordReplayFunction(CTFontGetBoundingRectsForGlyphs, CGRect, aFont, aOrientation, - aGlyphs, aRects, aCount); - events.RecordOrReplayBytes(&rval, sizeof(CGRect)); - events.CheckInput(!!aRects); - if (aRects) { - events.CheckInput(aCount); - events.RecordOrReplayBytes(aRects, aCount * sizeof(CGRect)); - } - return rval; -} - -RRFunctionTypes1(CGPathGetCurrentPoint, CGPoint, CGPathRef) -RRFunction1(CGPathIsEmpty) -RRFunction1(CGSSetDebugOptions) -RRFunctionVoid0(CGSShutdownServerConnections) -RRFunction1(CTFontCopyFamilyName) -RRFunction1(CTFontCopyFontDescriptor) -RRFunction2(CTFontCopyGraphicsFont) -RRFunction3(CTFontCopyTable) -RRFunction1(CTFontCopyVariationAxes) -RRFunctionTypes3(CTFontCreateForString, CTFontRef, - CTFontRef, CFStringRef, CFRange) -RRFunction3(CTFontCreatePathForGlyph) -RRFunctionTypes3(CTFontCreateWithFontDescriptor, CTFontRef, - CTFontDescriptorRef, CGFloat, CGAffineTransform*) -RRFunctionTypes4(CTFontCreateWithGraphicsFont, CTFontRef, - CGFontRef, CGFloat, CGAffineTransform*, CTFontDescriptorRef) -RRFunctionTypes3(CTFontCreateWithName, CTFontRef, - CFStringRef, CGFloat, CGAffineTransform*) - -static void -RR_CTFontDrawGlyphs(CTFontRef aFont, CGGlyph* aGlyphs, CGPoint* aPositions, - size_t aCount, CGContextRef aContext) -{ - RecordReplayFunctionVoid(CTFontDrawGlyphs, aFont, aGlyphs, aPositions, aCount, aContext); - FlushContext(aContext); -} - -RRFunction2(CTFontDescriptorCopyAttribute) -RRFunction2(CTFontDescriptorCreateCopyWithAttributes) -RRFunction2(CTFontDescriptorCreateMatchingFontDescriptors) -RRFunction1(CTFontDescriptorCreateWithAttributes) - -static double -RR_CTFontGetAdvancesForGlyphs(CTFontRef aFont, CTFontOrientation aOrientation, - const CGGlyph* aGlyphs, CGSize* aAdvances, CFIndex aCount) -{ - RecordReplayFunction(CTFontGetAdvancesForGlyphs, double, aFont, aOrientation, - aGlyphs, aAdvances, aCount); - events.RecordOrReplayBytes(&rval, sizeof(double)); - events.CheckInput(!!aAdvances); - if (aAdvances) { - events.CheckInput(aCount); - events.RecordOrReplayBytes(aAdvances, aCount * sizeof(CGSize)); - } - return rval; -} - -RRFunctionTypes1(CTFontGetAscent, CGFloat, CTFontRef) -RRFunctionTypes1(CTFontGetBoundingBox, CGRect, CTFontRef) -RRFunctionTypes1(CTFontGetCapHeight, CGFloat, CTFontRef) -RRFunctionTypes1(CTFontGetDescent, CGFloat, CTFontRef) -RRFunction1(CTFontGetGlyphCount) - -static bool -RR_CTFontGetGlyphsForCharacters(CTFontRef aFont, const UniChar* aCharacters, - CGGlyph* aGlyphs, CFIndex aCount) -{ - RecordReplayFunction(CTFontGetGlyphsForCharacters, bool, aFont, aCharacters, aGlyphs, aCount); - events.RecordOrReplayValue(&rval); - events.CheckInput(aCount); - events.RecordOrReplayBytes(aGlyphs, aCount * sizeof(CGGlyph)); - return rval; -} - -RRFunctionTypes1(CTFontGetLeading, CGFloat, CTFontRef) -RRFunctionTypes1(CTFontGetSize, CGFloat, CTFontRef) -RRFunction1(CTFontGetSymbolicTraits) -RRFunctionTypes1(CTFontGetUnderlinePosition, CGFloat, CTFontRef) -RRFunctionTypes1(CTFontGetUnderlineThickness, CGFloat, CTFontRef) -RRFunction1(CTFontGetUnitsPerEm) -RRFunctionTypes1(CTFontGetXHeight, CGFloat, CTFontRef) -RRFunction0(CTFontManagerCopyAvailableFontFamilyNames) -RRFunction3(CTFontManagerRegisterFontsForURLs) -RRFunctionVoid2(CTFontManagerSetAutoActivationSetting) -RRFunction1(CTLineCreateWithAttributedString) -RRFunction1(CTLineGetGlyphRuns) -RRFunction1(CTRunGetAttributes) -RRFunction1(CTRunGetGlyphCount) - // Note: We only redirect CTRunGetGlyphsPtr, not CTRunGetGlyphs. The latter may // be implemented with a loop that jumps back into the code we overwrite with a // jump, a pattern which ProcessRedirect.cpp does not handle. For the moment, // Gecko code only calls CTRunGetGlyphs if CTRunGetGlyphsPtr fails, so make // sure that CTRunGetGlyphsPtr always succeeds when it is being recorded. -static const CGGlyph* -RR_CTRunGetGlyphsPtr(CTRunRef aRun) -{ - RecordReplayFunction(CTRunGetGlyphsPtr, CGGlyph*, aRun); - size_t numGlyphs; - if (IsRecording()) { - AutoPassThroughThreadEvents pt; - numGlyphs = CTRunGetGlyphCount(aRun); - if (!rval) { - rval = NewLeakyArray(numGlyphs); - CTRunGetGlyphs(aRun, CFRangeMake(0, 0), rval); - } - } - events.RecordOrReplayValue(&numGlyphs); - if (IsReplaying()) { - rval = NewLeakyArray(numGlyphs); - } - events.RecordOrReplayBytes(rval, numGlyphs * sizeof(CGGlyph)); - return rval; -} - -// Note: The same concerns apply here as for CTRunGetGlyphsPtr. -static const CGPoint* -RR_CTRunGetPositionsPtr(CTRunRef aRun) -{ - RecordReplayFunction(CTRunGetPositionsPtr, CGPoint*, aRun); - size_t numGlyphs; - if (IsRecording()) { - AutoPassThroughThreadEvents pt; - numGlyphs = CTRunGetGlyphCount(aRun); - if (!rval) { - rval = NewLeakyArray(numGlyphs); - CTRunGetPositions(aRun, CFRangeMake(0, 0), rval); - } - } - events.RecordOrReplayValue(&numGlyphs); - if (IsReplaying()) { - rval = NewLeakyArray(numGlyphs); - } - events.RecordOrReplayBytes(rval, numGlyphs * sizeof(CGPoint)); - return rval; -} - -// Note: The same concerns apply here as for CTRunGetGlyphsPtr. -static const CFIndex* -RR_CTRunGetStringIndicesPtr(CTRunRef aRun) -{ - RecordReplayFunction(CTRunGetStringIndicesPtr, CFIndex*, aRun); - size_t numGlyphs; - if (IsRecording()) { - AutoPassThroughThreadEvents pt; - numGlyphs = CTRunGetGlyphCount(aRun); - if (!rval) { - rval = NewLeakyArray(numGlyphs); - CTRunGetStringIndices(aRun, CFRangeMake(0, 0), rval); - } - } - events.RecordOrReplayValue(&numGlyphs); - if (IsReplaying()) { - rval = NewLeakyArray(numGlyphs); - } - events.RecordOrReplayBytes(rval, numGlyphs * sizeof(CFIndex)); - return rval; -} - -RRFunctionTypes1(CTRunGetStringRange, CFRange, CTRunRef) - -static double -RR_CTRunGetTypographicBounds(CTRunRef aRun, CFRange aRange, - CGFloat* aAscent, CGFloat* aDescent, CGFloat* aLeading) -{ - RecordReplayFunction(CTRunGetTypographicBounds, double, - aRun, aRange, aAscent, aDescent, aLeading); - events.CheckInput((aAscent ? 1 : 0) | (aDescent ? 2 : 0) | (aLeading ? 4 : 0)); - events.RecordOrReplayBytes(&rval, sizeof(double)); - if (aAscent) { - events.RecordOrReplayBytes(aAscent, sizeof(CGFloat)); - } - if (aDescent) { - events.RecordOrReplayBytes(aDescent, sizeof(CGFloat)); - } - if (aLeading) { - events.RecordOrReplayBytes(aLeading, sizeof(CGFloat)); - } - return rval; -} - -RRFunctionTypesVoid5(CUIDraw, void*, CGRect, CGContextRef, CFDictionaryRef, CFDictionaryRef*) -RRFunction2(FSCompareFSRefs) - -static OSErr -RR_FSGetVolumeInfo(size_t a0, size_t a1, size_t a2, size_t a3, size_t a4, - HFSUniStr255* aVolumeName, FSRef* aFsRef) -{ - RecordReplayFunction(FSGetVolumeInfo, OSErr, a0, a1, a2, a3, a4, aVolumeName, aFsRef); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aVolumeName, sizeof(HFSUniStr255)); - events.RecordOrReplayBytes(aFsRef, sizeof(FSRef)); - return rval; -} - -static OSErr -RR_FSFindFolder(size_t a0, size_t a1, size_t a2, FSRef* aFsRef) -{ - RecordReplayFunction(FSFindFolder, OSErr, a0, a1, a2, aFsRef); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aFsRef, sizeof(FSRef)); - return rval; -} - -static OSErr -RR_Gestalt(OSType aSelector, SInt32* aResponse) -{ - RecordReplayFunction(Gestalt, OSErr, aSelector, aResponse); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayValue(aResponse); - return rval; -} - -RRFunction1(GetEventClass) -RRFunction0(GetCurrentEventQueue) - -static OSErr -RR_GetCurrentProcess(ProcessSerialNumber* aPSN) -{ - RecordReplayFunction(GetCurrentProcess, OSErr, aPSN); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aPSN, sizeof(*aPSN)); - return rval; -} - -RRFunction1(GetEventAttributes) -RRFunction0(GetEventDispatcherTarget) -RRFunction1(GetEventKind) -RRFunction5(HIThemeDrawButton) -RRFunction4(HIThemeDrawFrame) -RRFunction4(HIThemeDrawGroupBox) -RRFunction4(HIThemeDrawGrowBox) -RRFunction4(HIThemeDrawMenuBackground) -RRFunction6(HIThemeDrawMenuItem) -RRFunction5(HIThemeDrawMenuSeparator) -RRFunction4(HIThemeDrawSeparator) -RRFunction4(HIThemeDrawTabPane) -RRFunction4(HIThemeDrawTrack) - -static OSStatus -RR_HIThemeGetGrowBoxBounds(const HIPoint* aOrigin, const HIThemeGrowBoxDrawInfo* aDrawInfo, - HIRect* aBounds) -{ - RecordReplayFunction(HIThemeGetGrowBoxBounds, OSStatus, aOrigin, aDrawInfo, aBounds); - events.RecordOrReplayValue(&rval); - events.RecordOrReplayBytes(aBounds, sizeof(*aBounds)); - return rval; -} - -RRFunction4(HIThemeSetFill) -RRFunction5(IORegistryEntrySearchCFProperty) -RRFunction1(LSCopyAllHandlersForURLScheme) - -static OSStatus -RR_LSCopyApplicationForMIMEType(CFStringRef aInMIMEType, LSRolesMask aInRoleMask, - CFURLRef* aOutAppURL) -{ - RecordReplayFunction(LSCopyApplicationForMIMEType, OSStatus, - aInMIMEType, aInRoleMask, aOutAppURL); - events.CheckInput(!!aOutAppURL); - events.RecordOrReplayValue(&rval); - if (aOutAppURL) { - events.RecordOrReplayValue((size_t*)aOutAppURL); - } - return rval; -} - -static OSStatus -RR_LSCopyItemAttribute(const FSRef* aInItem, LSRolesMask aInRoles, - CFStringRef aInAttributeName, CFTypeRef* aOutValue) -{ - RecordReplayFunction(LSCopyItemAttribute, OSStatus, - aInItem, aInRoles, aInAttributeName, aOutValue); - events.CheckInput(!!aOutValue); - events.RecordOrReplayValue(&rval); - if (aOutValue) { - events.RecordOrReplayValue((size_t*)aOutValue); - } - return rval; -} - -static OSStatus -RR_LSCopyKindStringForMIMEType(CFStringRef aInMIMEType, CFStringRef* aOutKindString) -{ - RecordReplayFunction(LSCopyKindStringForMIMEType, OSStatus, aInMIMEType, aOutKindString); - events.CheckInput(!!aOutKindString); - events.RecordOrReplayValue(&rval); - if (aOutKindString) { - events.RecordOrReplayValue((size_t*)aOutKindString); - } - return rval; -} - +// The same concerns apply to CTRunGetPositionsPtr and CTRunGetStringIndicesPtr, +// so they are all handled here. +template static void -RecordReplayFSRefAndURLRef(Stream& aStream, OSStatus* aOutStatus, - FSRef* aOutAppRef, CFURLRef* aOutAppURL) +RR_CTRunGetElements(Stream& aEvents, CallArguments* aArguments, ErrorType* aError) { - aStream.CheckInput((aOutAppRef ? 1 : 0) | (aOutAppURL ? 2 : 0)); - aStream.RecordOrReplayValue(aOutStatus); - if (aOutAppRef) { - aStream.RecordOrReplayBytes(aOutAppRef, sizeof(FSRef)); + auto& run = aArguments->Arg<0, CTRunRef>(); + auto& rval = aArguments->Rval(); + + size_t count; + if (IsRecording()) { + AutoPassThroughThreadEvents pt; + count = CTRunGetGlyphCount(run); + if (!rval) { + rval = NewLeakyArray(count); + GetElemsFn(run, CFRangeMake(0, 0), rval); + } } - if (aOutAppURL) { - aStream.RecordOrReplayValue((size_t*)aOutAppURL); + aEvents.RecordOrReplayValue(&count); + if (IsReplaying()) { + rval = NewLeakyArray(count); } + aEvents.RecordOrReplayBytes(rval, count * sizeof(ElemType)); } -static OSStatus -RR_LSGetApplicationForInfo(OSType aInType, OSType aInCreator, - CFStringRef aInExtension, LSRolesMask aInRoleMask, - FSRef* aOutAppRef, CFURLRef* aOutAppURL) +static PreambleResult +Preamble_OSSpinLockLock(CallArguments* aArguments) { - RecordReplayFunction(LSGetApplicationForInfo, OSStatus, - aInType, aInCreator, aInExtension, aInRoleMask, aOutAppRef, aOutAppURL); - RecordReplayFSRefAndURLRef(events, &rval, aOutAppRef, aOutAppURL); - return rval; -} + auto& lock = aArguments->Arg<0, OSSpinLock*>(); -static OSStatus -RR_LSGetApplicationForURL(CFURLRef aInURL, LSRolesMask aInRoleMask, - FSRef* aOutAppRef, CFURLRef* aOutAppURL) -{ - RecordReplayFunction(LSGetApplicationForURL, OSStatus, - aInURL, aInRoleMask, aOutAppRef, aOutAppURL); - RecordReplayFSRefAndURLRef(events, &rval, aOutAppRef, aOutAppURL); - return rval; -} - -RRFunction1(NSClassFromString) -RRFunctionTypesVoid1(NSRectFill, CGRect /* Actually NSRect, but they appear to always be the same */) -RRFunction3(NSSearchPathForDirectoriesInDomains) -RRFunction1(NSSetFocusRingStyle) -RRFunction0(NSTemporaryDirectory) -RRFunction1(ReleaseEvent) - -static void -RR_OSSpinLockLock(OSSpinLock *lock) -{ // These spin locks never need to be recorded, but they are used by malloc // and can end up calling other recorded functions like mach_absolute_time, // so make sure events are passed through here. Note that we don't have to // redirect OSSpinLockUnlock, as it doesn't have these issues. AutoEnsurePassThroughThreadEventsUseStackPointer pt; OriginalCall(OSSpinLockLock, void, lock); + + return PreambleResult::Veto; } -RRFunction2(RemoveEventFromQueue) -RRFunction1(RetainEvent) -RRFunction2(SendEventToEventTarget) - -// These are not public APIs, but other redirected functions may be aliases for -// these which are dynamically installed on the first call in a way that our -// redirection mechanism doesn't completely account for. -RRFunction2(SLEventSourceCounterForEventType) -RRFunction1(SLDisplayCopyColorSpace) -RRFunction1(SLDisplayIOServicePort) -RRFunction0(SLMainDisplayID) -RRFunction1(SLSSetDenyWindowServerConnections) -RRFunctionVoid0(SLSShutdownServerConnections) - /////////////////////////////////////////////////////////////////////////////// // Redirection generation /////////////////////////////////////////////////////////////////////////////// -#define MAKE_REDIRECTION_ENTRY(aName) \ - { #aName, nullptr, (uint8_t*) RR_ ##aName }, +#define MAKE_REDIRECTION_ENTRY(aName, ...) \ + { #aName, nullptr, nullptr, __VA_ARGS__ }, Redirection gRedirections[] = { FOR_EACH_REDIRECTION(MAKE_REDIRECTION_ENTRY)