Bug 1374888 - Maintain a live shared libraries list in gecko on macOS, r=mstange
authorMichael Layzell <michael@thelayzells.com>
Mon, 10 Jul 2017 15:29:35 -0400
changeset 607733 e4bd519bfbfa9e3e9ae4a18bb63c4f0a57015715
parent 607732 77447df746ccdb06887713577d439775d9ff3c2f
child 607734 21899ff0548e2b05e9a85675d33ad3765bb424ca
push id68095
push userbmo:rbarker@mozilla.com
push dateWed, 12 Jul 2017 20:01:47 +0000
reviewersmstange
bugs1374888
milestone56.0a1
Bug 1374888 - Maintain a live shared libraries list in gecko on macOS, r=mstange MozReview-Commit-ID: 5QvrWujquIq
tools/profiler/core/platform.cpp
tools/profiler/core/shared-libraries-linux.cc
tools/profiler/core/shared-libraries-macos.cc
tools/profiler/core/shared-libraries-win32.cc
tools/profiler/public/shared-libraries.h
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -2170,16 +2170,18 @@ MozGlueLabelExit(PseudoStack* aPseudoSta
 
 void
 profiler_init(void* aStackTop)
 {
   LOG("profiler_init");
 
   MOZ_RELEASE_ASSERT(!CorePS::Exists());
 
+  SharedLibraryInfo::Initialize();
+
   uint32_t features =
 #if defined(GP_OS_android)
                       ProfilerFeature::Java |
 #endif
                       ProfilerFeature::JS |
                       ProfilerFeature::Leaf |
 #if defined(HAVE_NATIVE_UNWIND)
                       ProfilerFeature::StackWalk |
--- a/tools/profiler/core/shared-libraries-linux.cc
+++ b/tools/profiler/core/shared-libraries-linux.cc
@@ -204,8 +204,14 @@ SharedLibraryInfo SharedLibraryInfo::Get
       // We only expect to see one such entry.
       break;
     }
   }
 #endif
 
   return info;
 }
+
+void
+SharedLibraryInfo::Initialize()
+{
+  /* do nothing */
+}
--- a/tools/profiler/core/shared-libraries-macos.cc
+++ b/tools/profiler/core/shared-libraries-macos.cc
@@ -2,26 +2,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <AvailabilityMacros.h>
 #include <mach-o/arch.h>
 #include <mach-o/loader.h>
 #include <mach-o/dyld_images.h>
+#include <mach-o/dyld.h>
 #include <mach/task_info.h>
 #include <mach/task.h>
 #include <mach/mach_init.h>
 #include <mach/mach_traps.h>
+#include <dlfcn.h>
 #include <string.h>
 #include <stdlib.h>
 #include <vector>
 #include <sstream>
 #include "mozilla/Unused.h"
 #include "nsNativeCharsetUtils.h"
+#include "ClearOnShutdown.h"
 
 #include "shared-libraries.h"
 
 // Architecture specific abstraction.
 #if defined(GP_ARCH_x86)
 typedef mach_header platform_mach_header;
 typedef segment_command mach_segment_command_type;
 #define MACHO_MAGIC_NUMBER MH_MAGIC
@@ -30,18 +33,81 @@ typedef segment_command mach_segment_com
 #else
 typedef mach_header_64 platform_mach_header;
 typedef segment_command_64 mach_segment_command_type;
 #define MACHO_MAGIC_NUMBER MH_MAGIC_64
 #define CMD_SEGMENT LC_SEGMENT_64
 #define seg_size uint64_t
 #endif
 
+struct NativeSharedLibrary
+{
+  const platform_mach_header* header;
+  std::string path;
+};
+static std::vector<NativeSharedLibrary>* sSharedLibrariesList = nullptr;
+static StaticMutex sSharedLibrariesMutex;
+
+static void
+SharedLibraryAddImage(const struct mach_header* mh, intptr_t vmaddr_slide)
+{
+  // NOTE: Presumably for backwards-compatibility reasons, this function accepts
+  // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast
+  // it to the right type here.
+  auto header = reinterpret_cast<const platform_mach_header*>(mh);
+
+  Dl_info info;
+  if (!dladdr(header, &info)) {
+    return;
+  }
+
+  StaticMutexAutoLock lock(sSharedLibrariesMutex);
+  if (!sSharedLibrariesList) {
+    return;
+  }
+
+  NativeSharedLibrary lib = { header, info.dli_fname };
+  sSharedLibrariesList->push_back(lib);
+}
+
+static void
+SharedLibraryRemoveImage(const struct mach_header* mh, intptr_t vmaddr_slide)
+{
+  // NOTE: Presumably for backwards-compatibility reasons, this function accepts
+  // a mach_header even on 64-bit where it ought to be a mach_header_64. We cast
+  // it to the right type here.
+  auto header = reinterpret_cast<const platform_mach_header*>(mh);
+
+  StaticMutexAutoLock lock(sSharedLibrariesMutex);
+  if (!sSharedLibrariesList) {
+    return;
+  }
+
+  uint32_t count = sSharedLibrariesList->size();
+  for (uint32_t i = 0; i < count; ++i) {
+    if ((*sSharedLibrariesList)[i].header == header) {
+      sSharedLibrariesList->erase(sSharedLibrariesList->begin() + i);
+      return;
+    }
+  }
+}
+
+void
+SharedLibraryInfo::Initialize()
+{
+  // NOTE: We intentionally leak this memory here. We're allocating dynamically
+  // in order to avoid static initializers.
+  sSharedLibrariesList = new std::vector<NativeSharedLibrary>();
+
+  _dyld_register_func_for_add_image(SharedLibraryAddImage);
+  _dyld_register_func_for_remove_image(SharedLibraryRemoveImage);
+}
+
 static
-void addSharedLibrary(const platform_mach_header* header, char *path, SharedLibraryInfo &info) {
+void addSharedLibrary(const platform_mach_header* header, const char *path, SharedLibraryInfo &info) {
   const struct load_command *cmd =
     reinterpret_cast<const struct load_command *>(header + 1);
 
   seg_size size = 0;
   unsigned long long start = reinterpret_cast<unsigned long long>(header);
   // Find the cmd segment in the macho image. It will contain the offset we care about.
   const uint8_t *uuid_bytes = nullptr;
   for (unsigned int i = 0;
@@ -86,66 +152,22 @@ void addSharedLibrary(const platform_mac
     NXGetArchInfoFromCpuType(header->cputype, header->cpusubtype);
 
   info.AddSharedLibrary(SharedLibrary(start, start + size, 0, uuid.str(),
                                       nameStr, pathStr, nameStr, pathStr,
                                       "",
                                       archInfo ? archInfo->name : ""));
 }
 
-// Use dyld to inspect the macho image information. We can build the SharedLibraryEntry structure
-// giving us roughtly the same info as /proc/PID/maps in Linux.
-SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
+// Translate the statically stored sSharedLibrariesList information into a
+// SharedLibraryInfo object.
+SharedLibraryInfo
+SharedLibraryInfo::GetInfoForSelf()
 {
+  StaticMutexAutoLock lock(sSharedLibrariesMutex);
   SharedLibraryInfo sharedLibraryInfo;
 
-  const dyld_image_info* infoArray = nullptr;
-  size_t infoCount = 0;
-
-  // Getting the info array can fail if it is currently being modified, so we
-  // might have to try more than once. Attempt to get the library info for the
-  // currently loaded libraries 100 times before giving up. Between each run we
-  // sleep a bit to give the loader some time to finish the load.
-  int retryCount = 100;
-  while (!infoArray && --retryCount > 0) {
-    task_dyld_info_data_t task_dyld_info;
-    mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
-    if (task_info(mach_task_self (), TASK_DYLD_INFO, (task_info_t)&task_dyld_info,
-                  &count) != KERN_SUCCESS) {
-      return sharedLibraryInfo;
-    }
-
-    struct dyld_all_image_infos* aii =
-      (struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr;
-    infoCount = aii->infoArrayCount;
-    infoArray = aii->infoArray;
-
-    // If we don't have infoArray, that means that dyld is currently loading an
-    // array. Wait for a bit and then try again until we run out of retries.
-    if (!infoArray) {
-      // Set infoCount to 0 to ensure that we don't try to dereference the null
-      // infoArray if we run out of retries.
-      infoCount = 0;
-      PR_Sleep(0);
-    }
-  }
-
-  // Iterate through all dyld images (loaded libraries) to get their names
-  // and offests.
-  for (size_t i = 0; i < infoCount; ++i) {
-    const dyld_image_info& info = infoArray[i];
-
-    // If the magic number doesn't match then go no further
-    // since we're not pointing to where we think we are.
-    if (info.imageLoadAddress->magic != MACHO_MAGIC_NUMBER) {
-      continue;
-    }
-
-    const platform_mach_header* header =
-      reinterpret_cast<const platform_mach_header*>(info.imageLoadAddress);
-
-    // Add the entry for this image.
-    addSharedLibrary(header, (char*)info.imageFilePath, sharedLibraryInfo);
+  for (auto& info : *sSharedLibrariesList) {
+    addSharedLibrary(info.header, info.path.c_str(), sharedLibraryInfo);
   }
 
   return sharedLibraryInfo;
 }
-
--- a/tools/profiler/core/shared-libraries-win32.cc
+++ b/tools/profiler/core/shared-libraries-win32.cc
@@ -204,8 +204,13 @@ SharedLibraryInfo SharedLibraryInfo::Get
     sharedLibraryInfo.AddSharedLibrary(shlib);
 
     FreeLibrary(handleLock); // ok to free null handles
   }
 
   return sharedLibraryInfo;
 }
 
+void
+SharedLibraryInfo::Initialize()
+{
+  /* do nothing */
+}
--- a/tools/profiler/public/shared-libraries.h
+++ b/tools/profiler/public/shared-libraries.h
@@ -127,16 +127,18 @@ static bool
 CompareAddresses(const SharedLibrary& first, const SharedLibrary& second)
 {
   return first.GetStart() < second.GetStart();
 }
 
 class SharedLibraryInfo {
 public:
   static SharedLibraryInfo GetInfoForSelf();
+  static void Initialize();
+
   SharedLibraryInfo() {}
 
   void AddSharedLibrary(SharedLibrary entry)
   {
     mEntries.push_back(entry);
   }
 
   const SharedLibrary& GetEntry(size_t i) const