Bug 1509562 Part 3 - Remove dependence on CallEvent_* enum, r=lsmyth.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 23 Nov 2018 09:27:38 -1000
changeset 504625 d0efb6f89bd0b8d070ab4a1bf6fca4d328dcb670
parent 504624 bb62b5dc4dbec70a1645b59d6059726f50820ead
child 504626 b9f41dd78cfdd2739b343595c1647ef1c4f57875
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsmyth
bugs1509562
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1509562 Part 3 - Remove dependence on CallEvent_* enum, r=lsmyth.
toolkit/recordreplay/MiddlemanCall.cpp
toolkit/recordreplay/ProcessRecordReplay.cpp
toolkit/recordreplay/ProcessRedirect.cpp
toolkit/recordreplay/ProcessRedirect.h
toolkit/recordreplay/ProcessRedirectDarwin.cpp
--- a/toolkit/recordreplay/MiddlemanCall.cpp
+++ b/toolkit/recordreplay/MiddlemanCall.cpp
@@ -172,17 +172,17 @@ ProcessMiddlemanCall(const char* aInputD
     CallArguments arguments;
     call->mArguments.CopyTo(&arguments);
 
     {
       MiddlemanCallContext cx(call, &arguments, MiddlemanCallPhase::MiddlemanInput);
       redirection.mMiddlemanCall(cx);
     }
 
-    RecordReplayInvokeCall(call->mCallId, &arguments);
+    RecordReplayInvokeCall(redirection.mBaseFunction, &arguments);
 
     {
       MiddlemanCallContext cx(call, &arguments, MiddlemanCallPhase::MiddlemanOutput);
       redirection.mMiddlemanCall(cx);
     }
 
     call->mArguments.CopyFrom(&arguments);
     call->EncodeOutput(outputStream);
--- a/toolkit/recordreplay/ProcessRecordReplay.cpp
+++ b/toolkit/recordreplay/ProcessRecordReplay.cpp
@@ -129,16 +129,17 @@ RecordReplayInterface_Initialize(int aAr
   } else {
     gInitializationFailureMessage = strdup("Bad recording file");
   }
 
   if (gInitializationFailureMessage) {
     fprintf(stderr, "Initialization Failure: %s\n", gInitializationFailureMessage);
   }
 
+  LateInitializeRedirections();
   Thread::InitializeThreads();
 
   Thread* thread = Thread::GetById(MainThreadId);
   MOZ_ASSERT(thread->Id() == MainThreadId);
 
   thread->BindToCurrent();
   thread->SetPassThrough(true);
 
--- a/toolkit/recordreplay/ProcessRedirect.cpp
+++ b/toolkit/recordreplay/ProcessRedirect.cpp
@@ -34,17 +34,17 @@ static bool
 CallPreambleHook(PreambleFn aPreamble, size_t aCallId, CallArguments* aArguments)
 {
   PreambleResult result = aPreamble(aArguments);
   switch (result) {
   case PreambleResult::Veto:
     return true;
   case PreambleResult::PassThrough: {
     AutoEnsurePassThroughThreadEvents pt;
-    RecordReplayInvokeCall(aCallId, aArguments);
+    RecordReplayInvokeCall(OriginalFunction(aCallId), aArguments);
     return true;
   }
   case PreambleResult::Redirect:
     return false;
   }
   Unreachable();
 }
 
@@ -115,17 +115,17 @@ RecordReplayInterceptCall(int aCallId, C
   }
 
   if (IsRecording()) {
     // Call the original function, passing through events while we do so.
     // Destroy the RecordingEventSection so that we don't prevent the file
     // from being flushed in case we end up blocking.
     res.reset();
     thread->SetPassThrough(true);
-    RecordReplayInvokeCall(aCallId, aArguments);
+    RecordReplayInvokeCall(redirection.mOriginalFunction, aArguments);
     thread->SetPassThrough(false);
     res.emplace(thread);
   }
 
   // Save any system error in case we want to record/replay it.
   ErrorType error = SaveError();
 
   // Add an event for the thread.
@@ -282,19 +282,19 @@ RecordReplayInvokeCallRaw(CallArguments*
   "movsd %xmm1, 96(%rdi);"
 
   "ret;"
 );
 
 } // extern "C"
 
 MOZ_NEVER_INLINE void
-RecordReplayInvokeCall(size_t aCallId, CallArguments* aArguments)
+RecordReplayInvokeCall(void* aFunction, CallArguments* aArguments)
 {
-  RecordReplayInvokeCallRaw(aArguments, OriginalFunction(aCallId));
+  RecordReplayInvokeCallRaw(aArguments, aFunction);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Library API Redirections
 ///////////////////////////////////////////////////////////////////////////////
 
 // Redirecting system library APIs requires delicacy. We have to patch the code
 // so that whenever control reaches the beginning of the library API's symbol,
@@ -948,16 +948,29 @@ InitializeRedirections()
     for (uint8_t* ip = patch.mStart; ip < patch.mEnd; ip++) {
       Assembler::PatchClobber(ip);
     }
   }
 
   return true;
 }
 
+void*
+OriginalFunction(const char* aName)
+{
+  size_t numRedirections = NumRedirections();
+  for (size_t i = 0; i < numRedirections; i++) {
+    const Redirection& redirection = GetRedirection(i);
+    if (!strcmp(aName, redirection.mName)) {
+      return redirection.mOriginalFunction;
+    }
+  }
+  MOZ_CRASH("OriginalFunction: unknown redirection");
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Utility
 ///////////////////////////////////////////////////////////////////////////////
 
 Atomic<size_t, SequentiallyConsistent, Behavior::DontPreserve> gMemoryLeakBytes;
 
 void*
 BindFunctionArgument(void* aFunction, void* aArgument, size_t aArgumentPosition,
--- a/toolkit/recordreplay/ProcessRedirect.h
+++ b/toolkit/recordreplay/ProcessRedirect.h
@@ -264,16 +264,20 @@ Redirection& GetRedirection(size_t aCall
 // recording/replaying and middleman processes, and allows OriginalCall() to
 // work in either case.
 void EarlyInitializeRedirections();
 
 // Set up all platform specific redirections, or fail and set
 // gInitializationFailureMessage.
 bool InitializeRedirections();
 
+// Platform specific function called after setting up redirections in recording
+// or replaying processes.
+void LateInitializeRedirections();
+
 // Functions for saving or restoring system error codes.
 static inline ErrorType SaveError() { return errno; }
 static inline void RestoreError(ErrorType aError) { errno = aError; }
 
 // Specify the default ABI to use by the record/replay macros below.
 #define DEFAULTABI
 
 // Define CallFunction(...) for all supported ABIs.
@@ -281,36 +285,27 @@ DefineAllCallFunctions(DEFAULTABI)
 
 // Get the address of the original function for a call event ID.
 static inline void*
 OriginalFunction(size_t aCallId)
 {
   return GetRedirection(aCallId).mOriginalFunction;
 }
 
-#define TokenPaste(aFirst, aSecond) aFirst ## aSecond
-
-// Call the original function for a call event ID with a particular ABI and any
-// number of arguments.
-#define OriginalCallABI(aName, aReturnType, aABI, ...)          \
-  TokenPaste(CallFunction, aABI) <aReturnType>                  \
-    (OriginalFunction(CallEvent_ ##aName), ##__VA_ARGS__)
-
-// Call the original function for a call event ID with the default ABI.
-#define OriginalCall(aName, aReturnType, ...)                   \
-  OriginalCallABI(aName, aReturnType, DEFAULTABI, ##__VA_ARGS__)
+// Get the address of the original function by name.
+void* OriginalFunction(const char* aName);
 
 static inline ThreadEvent
 CallIdToThreadEvent(size_t aCallId)
 {
   return (ThreadEvent)((uint32_t)ThreadEvent::CallStart + aCallId);
 }
 
 void
-RecordReplayInvokeCall(size_t aCallId, CallArguments* aArguments);
+RecordReplayInvokeCall(void* aFunction, CallArguments* aArguments);
 
 ///////////////////////////////////////////////////////////////////////////////
 // Callback Redirections
 ///////////////////////////////////////////////////////////////////////////////
 
 // Below are helpers for use in handling a common callback pattern used within
 // redirections: the system is passed a pointer to a Gecko callback, and a
 // pointer to some opaque Gecko data which the system will pass to the callback
--- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp
+++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp
@@ -713,16 +713,57 @@ namespace recordreplay {
 enum CallEvent {                                \
   FOR_EACH_REDIRECTION(MAKE_CALL_EVENT)         \
   CallEvent_Count                               \
 };
 
 #undef MAKE_CALL_EVENT
 
 ///////////////////////////////////////////////////////////////////////////////
+// Original functions
+///////////////////////////////////////////////////////////////////////////////
+
+// Specify all the redirections for which the original function (with its
+// normal non-redirected semantics) is needed.
+#define FOR_EACH_ORIGINAL_FUNCTION(MACRO)                               \
+  MACRO(__workq_kernreturn)                                             \
+  MACRO(CFDataGetLength)                                                \
+  MACRO(CGPathApply)                                                    \
+  MACRO(close)                                                          \
+  MACRO(lseek)                                                          \
+  MACRO(mach_absolute_time)                                             \
+  MACRO(mmap)                                                           \
+  MACRO(mprotect)                                                       \
+  MACRO(munmap)                                                         \
+  MACRO(objc_msgSend)                                                   \
+  MACRO(open)                                                           \
+  MACRO(OSSpinLockLock)                                                 \
+  MACRO(pipe)                                                           \
+  MACRO(PL_HashTableDestroy)                                            \
+  MACRO(pthread_cond_wait)                                              \
+  MACRO(pthread_cond_timedwait)                                         \
+  MACRO(pthread_cond_timedwait_relative_np)                             \
+  MACRO(pthread_create)                                                 \
+  MACRO(pthread_mutex_destroy)                                          \
+  MACRO(pthread_mutex_init)                                             \
+  MACRO(pthread_mutex_lock)                                             \
+  MACRO(pthread_mutex_trylock)                                          \
+  MACRO(pthread_mutex_unlock)                                           \
+  MACRO(read)                                                           \
+  MACRO(start_wqthread)                                                 \
+  MACRO(write)
+
+#define DECLARE_ORIGINAL_FUNCTION(aName)        \
+  static void* gOriginal_ ##aName;
+
+  FOR_EACH_ORIGINAL_FUNCTION(DECLARE_ORIGINAL_FUNCTION)
+
+#undef DECLARE_ORIGINAL_FUNCTION
+
+///////////////////////////////////////////////////////////////////////////////
 // Callbacks
 ///////////////////////////////////////////////////////////////////////////////
 
 enum CallbackEvent {
   CallbackEvent_CFRunLoopPerformCallBack,
   CallbackEvent_CGPathApplierFunction
 };
 
@@ -1071,31 +1112,31 @@ Preamble_mmap(CallArguments* aArguments)
 
   void* memory = nullptr;
   if ((flags & MAP_ANON) || (IsReplaying() && !AreThreadEventsPassedThrough())) {
     // Get an anonymous mapping for the result.
     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, address, size, PROT_READ | PROT_WRITE | PROT_EXEC);
+        CallFunction<int>(gOriginal_mprotect, address, size, PROT_READ | PROT_WRITE | PROT_EXEC);
       }
       memset(address, 0, size);
       memory = address;
     } else {
       memory = AllocateMemoryTryAddress(address, RoundupSizeToPageBoundary(size),
                                         MemoryKind::Tracked);
     }
   } else {
     // We have to call mmap itself, which can change memory protection flags
     // 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) : prot;
-    memory = OriginalCall(mmap, void*, address, size, newProt, flags, fd, offset);
+    memory = CallFunction<void*>(gOriginal_mmap, address, size, newProt, flags, fd, offset);
 
     if (flags & MAP_FIXED) {
       MOZ_RELEASE_ASSERT(memory == address);
       RestoreWritableFixedMemory(memory, RoundupSizeToPageBoundary(size));
     } else if (memory && memory != (void*)-1) {
       RegisterAllocatedMemory(memory, RoundupSizeToPageBoundary(size), MemoryKind::Tracked);
     }
   }
@@ -1239,52 +1280,52 @@ Preamble___workq_kernreturn(CallArgument
   // Busy-wait until initialization is complete.
   while (!gInitialized) {
     ThreadYield();
   }
 
   // Make sure we know this thread exists.
   Thread::Current();
 
-  RecordReplayInvokeCall(CallEvent___workq_kernreturn, aArguments);
+  RecordReplayInvokeCall(gOriginal___workq_kernreturn, aArguments);
   return PreambleResult::Veto;
 }
 
 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.
   // Use this redirection to watch for new threads being spawned by GCD, and
   // suspend them immediately.
   if (IsReplaying()) {
     Thread::WaitForeverNoIdle();
   }
 
-  RecordReplayInvokeCall(CallEvent_start_wqthread, aArguments);
+  RecordReplayInvokeCall(gOriginal_start_wqthread, aArguments);
   return PreambleResult::Veto;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // pthreads redirections
 ///////////////////////////////////////////////////////////////////////////////
 
 static void
 DirectLockMutex(pthread_mutex_t* aMutex)
 {
   AutoPassThroughThreadEvents pt;
-  ssize_t rv = OriginalCall(pthread_mutex_lock, ssize_t, aMutex);
+  ssize_t rv = CallFunction<ssize_t>(gOriginal_pthread_mutex_lock, aMutex);
   MOZ_RELEASE_ASSERT(rv == 0);
 }
 
 static void
 DirectUnlockMutex(pthread_mutex_t* aMutex)
 {
   AutoPassThroughThreadEvents pt;
-  ssize_t rv = OriginalCall(pthread_mutex_unlock, ssize_t, aMutex);
+  ssize_t rv = CallFunction<ssize_t>(gOriginal_pthread_mutex_unlock, aMutex);
   MOZ_RELEASE_ASSERT(rv == 0);
 }
 
 // Handle a redirection which releases a mutex, waits in some way for a cvar,
 // and reacquires the mutex before returning.
 static ssize_t
 WaitForCvar(pthread_mutex_t* aMutex, pthread_cond_t* aCond, bool aRecordReturnValue,
             const std::function<ssize_t()>& aCallback)
@@ -1333,43 +1374,43 @@ WaitForCvar(pthread_mutex_t* aMutex, pth
 
 static PreambleResult
 Preamble_pthread_cond_wait(CallArguments* aArguments)
 {
   auto& cond = aArguments->Arg<0, pthread_cond_t*>();
   auto& mutex = aArguments->Arg<1, pthread_mutex_t*>();
   aArguments->Rval<ssize_t>() =
     WaitForCvar(mutex, cond, false,
-                [=]() { return OriginalCall(pthread_cond_wait, ssize_t, cond, mutex); });
+                [=]() { return CallFunction<ssize_t>(gOriginal_pthread_cond_wait, cond, mutex); });
   return PreambleResult::Veto;
 }
 
 static PreambleResult
 Preamble_pthread_cond_timedwait(CallArguments* aArguments)
 {
   auto& cond = aArguments->Arg<0, pthread_cond_t*>();
   auto& mutex = aArguments->Arg<1, pthread_mutex_t*>();
   auto& timeout = aArguments->Arg<2, timespec*>();
   aArguments->Rval<ssize_t>() =
     WaitForCvar(mutex, cond, true,
-                [=]() { return OriginalCall(pthread_cond_timedwait, ssize_t,
-                                            cond, mutex, timeout); });
+                [=]() { return CallFunction<ssize_t>(gOriginal_pthread_cond_timedwait,
+                                                     cond, mutex, timeout); });
   return PreambleResult::Veto;
 }
 
 static PreambleResult
 Preamble_pthread_cond_timedwait_relative_np(CallArguments* aArguments)
 {
   auto& cond = aArguments->Arg<0, pthread_cond_t*>();
   auto& mutex = aArguments->Arg<1, pthread_mutex_t*>();
   auto& timeout = aArguments->Arg<2, timespec*>();
   aArguments->Rval<ssize_t>() =
     WaitForCvar(mutex, cond, true,
-                [=]() { return OriginalCall(pthread_cond_timedwait_relative_np, ssize_t,
-                                            cond, mutex, timeout); });
+                [=]() { return CallFunction<ssize_t>(gOriginal_pthread_cond_timedwait_relative_np,
+                                                     cond, mutex, timeout); });
   return PreambleResult::Veto;
 }
 
 static PreambleResult
 Preamble_pthread_create(CallArguments* aArguments)
 {
   if (AreThreadEventsPassedThrough()) {
     return PreambleResult::Redirect;
@@ -1416,45 +1457,45 @@ Preamble_pthread_join(CallArguments* aAr
 
 static PreambleResult
 Preamble_pthread_mutex_init(CallArguments* aArguments)
 {
   auto& mutex = aArguments->Arg<0, pthread_mutex_t*>();
   auto& attr = aArguments->Arg<1, pthread_mutexattr_t*>();
 
   Lock::New(mutex);
-  aArguments->Rval<ssize_t>() = OriginalCall(pthread_mutex_init, ssize_t, mutex, attr);
+  aArguments->Rval<ssize_t>() = CallFunction<ssize_t>(gOriginal_pthread_mutex_init, mutex, attr);
   return PreambleResult::Veto;
 }
 
 static PreambleResult
 Preamble_pthread_mutex_destroy(CallArguments* aArguments)
 {
   auto& mutex = aArguments->Arg<0, pthread_mutex_t*>();
 
   Lock::Destroy(mutex);
-  aArguments->Rval<ssize_t>() = OriginalCall(pthread_mutex_destroy, ssize_t, mutex);
+  aArguments->Rval<ssize_t>() = CallFunction<ssize_t>(gOriginal_pthread_mutex_destroy, mutex);
   return PreambleResult::Veto;
 }
 
 static PreambleResult
 Preamble_pthread_mutex_lock(CallArguments* aArguments)
 {
   auto& mutex = aArguments->Arg<0, pthread_mutex_t*>();
 
   Lock* lock = Lock::Find(mutex);
   if (!lock) {
     AutoEnsurePassThroughThreadEventsUseStackPointer pt;
-    aArguments->Rval<ssize_t>() = OriginalCall(pthread_mutex_lock, ssize_t, mutex);
+    aArguments->Rval<ssize_t>() = CallFunction<ssize_t>(gOriginal_pthread_mutex_lock, mutex);
     return PreambleResult::Veto;
   }
   ssize_t rv = 0;
   if (IsRecording()) {
     AutoPassThroughThreadEvents pt;
-    rv = OriginalCall(pthread_mutex_lock, ssize_t, mutex);
+    rv = CallFunction<ssize_t>(gOriginal_pthread_mutex_lock, mutex);
   }
   rv = RecordReplayValue(rv);
   MOZ_RELEASE_ASSERT(rv == 0 || rv == EDEADLK);
   if (rv == 0) {
     lock->Enter();
     if (IsReplaying()) {
       DirectLockMutex(mutex);
     }
@@ -1466,23 +1507,23 @@ Preamble_pthread_mutex_lock(CallArgument
 static PreambleResult
 Preamble_pthread_mutex_trylock(CallArguments* aArguments)
 {
   auto& mutex = aArguments->Arg<0, pthread_mutex_t*>();
 
   Lock* lock = Lock::Find(mutex);
   if (!lock) {
     AutoEnsurePassThroughThreadEvents pt;
-    aArguments->Rval<ssize_t>() = OriginalCall(pthread_mutex_trylock, ssize_t, mutex);
+    aArguments->Rval<ssize_t>() = CallFunction<ssize_t>(gOriginal_pthread_mutex_trylock, mutex);
     return PreambleResult::Veto;
   }
   ssize_t rv = 0;
   if (IsRecording()) {
     AutoPassThroughThreadEvents pt;
-    rv = OriginalCall(pthread_mutex_trylock, ssize_t, mutex);
+    rv = CallFunction<ssize_t>(gOriginal_pthread_mutex_trylock, mutex);
   }
   rv = RecordReplayValue(rv);
   MOZ_RELEASE_ASSERT(rv == 0 || rv == EBUSY);
   if (rv == 0) {
     lock->Enter();
     if (IsReplaying()) {
       DirectLockMutex(mutex);
     }
@@ -1494,17 +1535,17 @@ Preamble_pthread_mutex_trylock(CallArgum
 static PreambleResult
 Preamble_pthread_mutex_unlock(CallArguments* aArguments)
 {
   auto& mutex = aArguments->Arg<0, pthread_mutex_t*>();
 
   Lock* lock = Lock::Find(mutex);
   if (!lock) {
     AutoEnsurePassThroughThreadEventsUseStackPointer pt;
-    aArguments->Rval<ssize_t>() = OriginalCall(pthread_mutex_unlock, ssize_t, mutex);
+    aArguments->Rval<ssize_t>() = CallFunction<ssize_t>(gOriginal_pthread_mutex_unlock, mutex);
     return PreambleResult::Veto;
   }
   lock->Exit();
   DirectUnlockMutex(mutex);
   aArguments->Rval<ssize_t>() = 0;
   return PreambleResult::Veto;
 }
 
@@ -1563,17 +1604,17 @@ Preamble_gmtime(CallArguments* aArgument
 }
 
 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()) {
-    aArguments->Rval<uint64_t>() = OriginalCall(mach_absolute_time, uint64_t);
+    aArguments->Rval<uint64_t>() = CallFunction<uint64_t>(gOriginal_mach_absolute_time);
     return PreambleResult::Veto;
   }
   return PreambleResult::Redirect;
 }
 
 static PreambleResult
 Preamble_mach_vm_allocate(CallArguments* aArguments)
 {
@@ -1669,17 +1710,17 @@ Preamble_PL_NewHashTable(CallArguments* 
 }
 
 static PreambleResult
 Preamble_PL_HashTableDestroy(CallArguments* aArguments)
 {
   auto& table = aArguments->Arg<0, PLHashTable*>();
 
   void* priv = table->allocPriv;
-  OriginalCall(PL_HashTableDestroy, void, table);
+  CallFunction<void>(gOriginal_PL_HashTableDestroy, table);
   DestroyPLHashTableCallbacks(priv);
   return PreambleResult::Veto;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Objective C redirections
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1704,17 +1745,17 @@ Preamble_objc_msgSend(CallArguments* aAr
     auto message = aArguments->Arg<1, const char*>();
 
     // Watch for some top level NSApplication messages that can cause Gecko
     // events to be processed.
     if (!strcmp(message, "run") ||
         !strcmp(message, "nextEventMatchingMask:untilDate:inMode:dequeue:"))
     {
       PassThroughThreadEventsAllowCallbacks([&]() {
-          RecordReplayInvokeCall(CallEvent_objc_msgSend, aArguments);
+          RecordReplayInvokeCall(gOriginal_objc_msgSend, aArguments);
         });
       RecordReplayBytes(&aArguments->Rval<size_t>(), sizeof(size_t));
       return PreambleResult::Veto;
     }
   }
   return PreambleResult::Redirect;
 }
 
@@ -2087,17 +2128,17 @@ Middleman_CFArrayGetValueAtIndex(Middlem
 
 static void
 RR_CFDataGetBytePtr(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
 {
   auto& rval = aArguments->Rval<UInt8*>();
 
   size_t len = 0;
   if (IsRecording()) {
-    len = OriginalCall(CFDataGetLength, size_t, aArguments->Arg<0, CFDataRef>());
+    len = CallFunction<size_t>(gOriginal_CFDataGetLength, aArguments->Arg<0, CFDataRef>());
   }
   aEvents.RecordOrReplayValue(&len);
   if (IsReplaying()) {
     rval = NewLeakyArray<UInt8>(len);
   }
   aEvents.RecordOrReplayBytes(rval, len);
 }
 
@@ -2411,17 +2452,17 @@ Preamble_CGPathApply(CallArguments* aArg
   auto& path = aArguments->Arg<0, CGPathRef>();
   auto& data = aArguments->Arg<1, void*>();
   auto& function = aArguments->Arg<2, CGPathApplierFunction>();
 
   RegisterCallbackData(BitwiseCast<void*>(function));
   RegisterCallbackData(data);
   PassThroughThreadEventsAllowCallbacks([&]() {
       CallbackWrapperData wrapperData(function, data);
-      OriginalCall(CGPathApply, void, path, &wrapperData, CGPathApplierFunctionWrapper);
+      CallFunction<void>(gOriginal_CGPathApply, path, &wrapperData, CGPathApplierFunctionWrapper);
     });
   RemoveCallbackData(data);
 
   return PreambleResult::Veto;
 }
 
 // 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
@@ -2484,17 +2525,17 @@ Preamble_OSSpinLockLock(CallArguments* a
 {
   auto& lock = aArguments->Arg<0, OSSpinLock*>();
 
   // 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);
+  CallFunction<void>(gOriginal_OSSpinLockLock, lock);
 
   return PreambleResult::Veto;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Redirection generation
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -2546,133 +2587,146 @@ EarlyInitializeRedirections()
 
     redirection.mBaseFunction = FunctionStartAddress(redirection);
     redirection.mOriginalFunction = redirection.mBaseFunction;
 
     if (redirection.mBaseFunction && IsRecordingOrReplaying()) {
       // We will get confused if we try to redirect the same address in multiple places.
       for (size_t j = 0; j < i; j++) {
         if (gRedirections[j].mBaseFunction == redirection.mBaseFunction) {
-          PrintSpew("Redirection %s shares the same address as %s, skipping.\n",
-                    redirection.mName, gRedirections[j].mName);
           redirection.mBaseFunction = nullptr;
           break;
         }
       }
     }
   }
+
+  // Bind the gOriginal functions to their redirections' base addresses until we
+  // finish installing redirections.
+  LateInitializeRedirections();
+}
+
+void
+LateInitializeRedirections()
+{
+#define INIT_ORIGINAL_FUNCTION(aName)        \
+  gOriginal_ ##aName = OriginalFunction(#aName);
+
+  FOR_EACH_ORIGINAL_FUNCTION(INIT_ORIGINAL_FUNCTION)
+
+#undef INIT_ORIGINAL_FUNCTION
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Direct system call API
 ///////////////////////////////////////////////////////////////////////////////
 
 const char*
 SymbolNameRaw(void* aPtr)
 {
   Dl_info info;
   return (dladdr(aPtr, &info) && info.dli_sname) ? info.dli_sname : "???";
 }
 
 void*
 DirectAllocateMemory(void* aAddress, size_t aSize)
 {
-  void* res = OriginalCall(mmap, void*,
-                           aAddress, aSize, PROT_READ | PROT_WRITE | PROT_EXEC,
-                           MAP_ANON | MAP_PRIVATE, -1, 0);
+  void* res = CallFunction<void*>(gOriginal_mmap, aAddress, aSize,
+                                  PROT_READ | PROT_WRITE | PROT_EXEC,
+                                  MAP_ANON | MAP_PRIVATE, -1, 0);
   MOZ_RELEASE_ASSERT(res && res != (void*)-1);
   return res;
 }
 
 void
 DirectDeallocateMemory(void* aAddress, size_t aSize)
 {
-  ssize_t rv = OriginalCall(munmap, int, aAddress, aSize);
+  ssize_t rv = CallFunction<int>(gOriginal_munmap, aAddress, aSize);
   MOZ_RELEASE_ASSERT(rv >= 0);
 }
 
 void
 DirectWriteProtectMemory(void* aAddress, size_t aSize, bool aExecutable,
                          bool aIgnoreFailures /* = false */)
 {
-  ssize_t rv = OriginalCall(mprotect, int, aAddress, aSize,
-                            PROT_READ | (aExecutable ? PROT_EXEC : 0));
+  ssize_t rv = CallFunction<int>(gOriginal_mprotect, aAddress, aSize,
+                                 PROT_READ | (aExecutable ? PROT_EXEC : 0));
   MOZ_RELEASE_ASSERT(aIgnoreFailures || rv == 0);
 }
 
 void
 DirectUnprotectMemory(void* aAddress, size_t aSize, bool aExecutable,
                       bool aIgnoreFailures /* = false */)
 {
-  ssize_t rv = OriginalCall(mprotect, int, aAddress, aSize,
-                            PROT_READ | PROT_WRITE | (aExecutable ? PROT_EXEC : 0));
+  ssize_t rv = CallFunction<int>(gOriginal_mprotect, aAddress, aSize,
+                                 PROT_READ | PROT_WRITE | (aExecutable ? PROT_EXEC : 0));
   MOZ_RELEASE_ASSERT(aIgnoreFailures || rv == 0);
 }
 
 void
 DirectSeekFile(FileHandle aFd, uint64_t aOffset)
 {
   static_assert(sizeof(uint64_t) == sizeof(off_t), "off_t should have 64 bits");
-  ssize_t rv = HANDLE_EINTR(OriginalCall(lseek, int, aFd, aOffset, SEEK_SET));
+  ssize_t rv = HANDLE_EINTR(CallFunction<int>(gOriginal_lseek, aFd, aOffset, SEEK_SET));
   MOZ_RELEASE_ASSERT(rv >= 0);
 }
 
 FileHandle
 DirectOpenFile(const char* aFilename, bool aWriting)
 {
   int flags = aWriting ? (O_WRONLY | O_CREAT | O_TRUNC) : O_RDONLY;
   int perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-  int fd = HANDLE_EINTR(OriginalCall(open, int, aFilename, flags, perms));
+  int fd = HANDLE_EINTR(CallFunction<int>(gOriginal_open, aFilename, flags, perms));
   MOZ_RELEASE_ASSERT(fd > 0);
   return fd;
 }
 
 void
 DirectCloseFile(FileHandle aFd)
 {
-  ssize_t rv = HANDLE_EINTR(OriginalCall(close, int, aFd));
+  ssize_t rv = HANDLE_EINTR(CallFunction<int>(gOriginal_close, aFd));
   MOZ_RELEASE_ASSERT(rv >= 0);
 }
 
 void
 DirectDeleteFile(const char* aFilename)
 {
   ssize_t rv = unlink(aFilename);
   MOZ_RELEASE_ASSERT(rv >= 0 || errno == ENOENT);
 }
 
 void
 DirectWrite(FileHandle aFd, const void* aData, size_t aSize)
 {
-  ssize_t rv = HANDLE_EINTR(OriginalCall(write, int, aFd, aData, aSize));
+  ssize_t rv = HANDLE_EINTR(CallFunction<int>(gOriginal_write, aFd, aData, aSize));
   MOZ_RELEASE_ASSERT((size_t) rv == aSize);
 }
 
 void
 DirectPrint(const char* aString)
 {
   DirectWrite(STDERR_FILENO, aString, strlen(aString));
 }
 
 size_t
 DirectRead(FileHandle aFd, void* aData, size_t aSize)
 {
   // Clear the memory in case it is write protected by the memory snapshot
   // mechanism.
   memset(aData, 0, aSize);
-  ssize_t rv = HANDLE_EINTR(OriginalCall(read, int, aFd, aData, aSize));
+  ssize_t rv = HANDLE_EINTR(CallFunction<int>(gOriginal_read, aFd, aData, aSize));
   MOZ_RELEASE_ASSERT(rv >= 0);
   return (size_t) rv;
 }
 
 void
 DirectCreatePipe(FileHandle* aWriteFd, FileHandle* aReadFd)
 {
   int fds[2];
-  ssize_t rv = OriginalCall(pipe, int, fds);
+  ssize_t rv = CallFunction<int>(gOriginal_pipe, fds);
   MOZ_RELEASE_ASSERT(rv >= 0);
   *aWriteFd = fds[1];
   *aReadFd = fds[0];
 }
 
 static double gAbsoluteToNanosecondsRate;
 
 void
@@ -2682,17 +2736,17 @@ InitializeCurrentTime()
   Nanoseconds rate = AbsoluteToNanoseconds(time);
   MOZ_RELEASE_ASSERT(!rate.hi);
   gAbsoluteToNanosecondsRate = rate.lo / 1000000.0;
 }
 
 double
 CurrentTime()
 {
-  return OriginalCall(mach_absolute_time, int64_t) * gAbsoluteToNanosecondsRate / 1000.0;
+  return CallFunction<int64_t>(gOriginal_mach_absolute_time) * gAbsoluteToNanosecondsRate / 1000.0;
 }
 
 void
 DirectSpawnThread(void (*aFunction)(void*), void* aArgument)
 {
   MOZ_RELEASE_ASSERT(IsMiddleman() || AreThreadEventsPassedThrough());
 
   pthread_attr_t attr;
@@ -2701,17 +2755,18 @@ DirectSpawnThread(void (*aFunction)(void
 
   rv = pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);
   MOZ_RELEASE_ASSERT(rv == 0);
 
   rv = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
   MOZ_RELEASE_ASSERT(rv == 0);
 
   pthread_t pthread;
-  rv = OriginalCall(pthread_create, int, &pthread, &attr, (void* (*)(void*)) aFunction, aArgument);
+  rv = CallFunction<int>(gOriginal_pthread_create,
+                         &pthread, &attr, (void* (*)(void*)) aFunction, aArgument);
   MOZ_RELEASE_ASSERT(rv == 0);
 
   rv = pthread_attr_destroy(&attr);
   MOZ_RELEASE_ASSERT(rv == 0);
 }
 
 } // recordreplay
 } // mozilla