Bug 1174785 - Part 1: Add LogModule, LogModuleManager, and LazyLogModule. r=froydnj
authorEric Rahm <erahm@mozilla.com>
Mon, 19 Oct 2015 12:22:11 -0700
changeset 303814 342c95afb454a19b82bbfebdef8bd289cac157ea
parent 303813 fd36ee40d1227eb0f7ec0a791f8860364518f325
child 303815 f9cf413cb3da8c191c2f1ba34b605aa73b2a8e48
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1174785
milestone44.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 1174785 - Part 1: Add LogModule, LogModuleManager, and LazyLogModule. r=froydnj The logging interface is moved to xpcom/base, a LogModule wrapper for PR_Log is added, a thread-safe LogModuleManager is added, and a LazyLogModule class used to lazily load log modules in a thread-safe manner is added.
js/xpconnect/src/XPCShellImpl.cpp
toolkit/xre/nsEmbedFunctions.cpp
xpcom/base/Logging.cpp
xpcom/base/Logging.h
xpcom/base/moz.build
xpcom/build/XPCOMInit.cpp
xpcom/glue/Logging.h
xpcom/glue/moz.build
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1249,16 +1249,18 @@ XRE_XPCShellMain(int argc, char** argv, 
     nsresult rv;
 
     gErrFile = stderr;
     gOutFile = stdout;
     gInFile = stdin;
 
     NS_LogInit();
 
+    mozilla::LogModule::Init();
+
     // A initializer to initialize histogram collection
     // used by telemetry.
     UniquePtr<base::StatisticsRecorder> telStats =
        MakeUnique<base::StatisticsRecorder>();
 
     if (PR_GetEnv("MOZ_CHAOSMODE")) {
         ChaosFeature feature = ChaosFeature::Any;
         long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16);
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -345,16 +345,18 @@ XRE_InitChildProcess(int aArgc,
     if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1)
         freopen("CONIN$", "r", stdin);
   }
 #endif
 
   // NB: This must be called before profiler_init
   NS_LogInit();
 
+  mozilla::LogModule::Init();
+
   char aLocal;
   profiler_init(&aLocal);
 
   PROFILER_LABEL("Startup", "XRE_InitChildProcess",
     js::ProfileEntry::Category::OTHER);
 
   // Complete 'task_t' exchange for Mac OS X. This structure has the same size
   // regardless of architecture so we don't have any cross-arch issues here.
new file mode 100644
--- /dev/null
+++ b/xpcom/base/Logging.cpp
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "mozilla/Logging.h"
+
+#include <algorithm>
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include "nsClassHashtable.h"
+
+// NB: Initial amount determined by auditing the codebase for the total amount
+//     of unique module names and padding up to the next power of 2.
+const uint32_t kInitialModuleCount = 256;
+
+namespace mozilla {
+/**
+ * Safely converts an integer into a valid LogLevel.
+ */
+LogLevel
+Clamp(int32_t aLevel)
+{
+  aLevel = std::min(aLevel, static_cast<int32_t>(LogLevel::Verbose));
+  aLevel = std::max(aLevel, static_cast<int32_t>(LogLevel::Disabled));
+  return static_cast<LogLevel>(aLevel);
+}
+
+class LogModuleManager
+{
+public:
+  LogModuleManager()
+    : mModulesLock("logmodules")
+    , mModules(kInitialModuleCount)
+  {
+  }
+
+  ~LogModuleManager()
+  {
+    // NB: mModules owns all of the log modules, they will get destroyed by
+    //     its destructor.
+  }
+
+  LogModule* CreateOrGetModule(const char* aName)
+  {
+    OffTheBooksMutexAutoLock guard(mModulesLock);
+    LogModule* module = nullptr;
+    if (!mModules.Get(aName, &module)) {
+      // Create the PRLogModule, this will read any env vars that set the log
+      // level ahead of time. The module is held internally by NSPR, so it's
+      // okay to drop the pointer when leaving this scope.
+      PRLogModuleInfo* prModule = PR_NewLogModule(aName);
+
+      // NSPR does not impose a restriction on the values that log levels can
+      // be. LogModule uses the LogLevel enum class so we must clamp the value
+      // to a max of Verbose.
+      LogLevel logLevel = Clamp(prModule->level);
+      module = new LogModule(logLevel);
+      mModules.Put(aName, module);
+    }
+
+    return module;
+  }
+
+private:
+  OffTheBooksMutex mModulesLock;
+  nsClassHashtable<nsCharPtrHashKey, LogModule> mModules;
+};
+
+StaticAutoPtr<LogModuleManager> sLogModuleManager;
+
+LogModule*
+LogModule::Get(const char* aName)
+{
+  // This is just a pass through to the LogModuleManager so
+  // that the LogModuleManager implementation can be kept internal.
+  MOZ_ASSERT(sLogModuleManager != nullptr);
+  return sLogModuleManager->CreateOrGetModule(aName);
+}
+
+void
+LogModule::Init()
+{
+  // NB: This method is not threadsafe; it is expected to be called very early
+  //     in startup prior to any other threads being run.
+  if (sLogModuleManager) {
+    // Already initialized.
+    return;
+  }
+
+  // NB: We intentionally do not register for ClearOnShutdown as that happens
+  //     before all logging is complete. And, yes, that means we leak, but
+  //     we're doing that intentionally.
+  sLogModuleManager = new LogModuleManager();
+}
+
+} // namespace mozilla
rename from xpcom/glue/Logging.h
rename to xpcom/base/Logging.h
--- a/xpcom/glue/Logging.h
+++ b/xpcom/base/Logging.h
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_logging_h
 #define mozilla_logging_h
 
 #include "prlog.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Likely.h"
 
 // This file is a placeholder for a replacement to the NSPR logging framework
 // that is defined in prlog.h. Currently it is just a pass through, but as
 // work progresses more functionality will be swapped out in favor of
 // mozilla logging implementations.
 
 namespace mozilla {
 
@@ -38,23 +40,109 @@ enum class LogLevel {
   Disabled = 0,
   Error,
   Warning,
   Info,
   Debug,
   Verbose,
 };
 
+class LogModule
+{
+public:
+  /**
+   * Retrieves the module with the given name. If it does not already exist
+   * it will be created.
+   *
+   * @param aName The name of the module.
+   * @return A log module for the given name. This may be shared.
+   */
+#if !defined(MOZILLA_XPCOMRT_API)
+  static LogModule* Get(const char* aName);
+#else
+  // For simplicity, libxpcomrt doesn't supoort logging.
+  static LogModule* Get(const char* aName) { return nullptr; }
+#endif
+
+  static void Init();
+
+  /**
+   * Indicates whether or not the given log level is enabled.
+   */
+  bool ShouldLog(LogLevel aLevel) const { return mLevel >= aLevel; }
+
+  /**
+   * Retrieves the log module's current level.
+   */
+  LogLevel Level() const { return mLevel; }
+
+private:
+  friend class LogModuleManager;
+
+  explicit LogModule(LogLevel aLevel) : mLevel(aLevel) {}
+
+  LogModule(LogModule&) = delete;
+  LogModule& operator=(const LogModule&) = delete;
+
+  Atomic<LogLevel, Relaxed> mLevel;
+};
+
+/**
+ * Helper class that lazy loads the given log module. This is safe to use for
+ * declaring static references to log modules and can be used as a replacement
+ * for accessing a LogModule directly.
+ *
+ * Example usage:
+ *   static LazyLogModule sLayoutLog("layout");
+ *
+ *   void Foo() {
+ *     MOZ_LOG(sLayoutLog, LogLevel::Verbose, ("Entering foo"));
+ *   }
+ */
+class LazyLogModule final
+{
+public:
+  explicit MOZ_CONSTEXPR LazyLogModule(const char* aLogName)
+    : mLogName(aLogName)
+    , mLog(nullptr)
+  {
+  }
+
+  operator LogModule*()
+  {
+    // NB: The use of an atomic makes the reading and assignment of mLog
+    //     thread-safe. There is a small chance that mLog will be set more
+    //     than once, but that's okay as it will be set to the same LogModule
+    //     instance each time. Also note LogModule::Get is thread-safe.
+    LogModule* tmp = mLog;
+    if (MOZ_UNLIKELY(!tmp)) {
+      tmp = LogModule::Get(mLogName);
+      mLog = tmp;
+    }
+
+    return tmp;
+  }
+
+private:
+  const char* const mLogName;
+  Atomic<LogModule*, ReleaseAcquire> mLog;
+};
+
 namespace detail {
 
 inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
   MOZ_ASSERT(level != LogLevel::Disabled);
   return module && module->level >= static_cast<int>(level);
 }
 
+inline bool log_test(const LogModule* module, LogLevel level) {
+  MOZ_ASSERT(level != LogLevel::Disabled);
+  return module && module->ShouldLog(level);
+}
+
 } // namespace detail
 
 } // namespace mozilla
 
 #define MOZ_LOG_TEST(_module,_level) mozilla::detail::log_test(_module, _level)
 
 #define MOZ_LOG(_module,_level,_args)     \
   PR_BEGIN_MACRO             \
--- a/xpcom/base/moz.build
+++ b/xpcom/base/moz.build
@@ -78,16 +78,17 @@ EXPORTS.mozilla += [
     'CycleCollectedJSRuntime.h',
     'Debug.h',
     'DebuggerOnGCRunnable.h',
     'DeferredFinalize.h',
     'ErrorNames.h',
     'HoldDropJSObjects.h',
     'JSObjectHolder.h',
     'LinuxUtils.h',
+    'Logging.h',
     'nsMemoryInfoDumper.h',
     'OwningNonNull.h',
     'StaticMutex.h',
     'StaticPtr.h',
     'SystemMemoryReporter.h',
 ]
 
 # nsDebugImpl isn't unified because we disable PGO so that NS_ABORT_OOM isn't
@@ -102,16 +103,17 @@ UNIFIED_SOURCES += [
     'ClearOnShutdown.cpp',
     'CycleCollectedJSRuntime.cpp',
     'Debug.cpp',
     'DebuggerOnGCRunnable.cpp',
     'DeferredFinalize.cpp',
     'ErrorNames.cpp',
     'HoldDropJSObjects.cpp',
     'JSObjectHolder.cpp',
+    'Logging.cpp',
     'nsConsoleMessage.cpp',
     'nsConsoleService.cpp',
     'nsCycleCollector.cpp',
     'nsDumpUtils.cpp',
     'nsErrorService.cpp',
     'nsGZFileWriter.cpp',
     'nsInterfaceRequestorAgg.cpp',
     'nsMemoryImpl.cpp',
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -493,16 +493,18 @@ NS_InitXPCOM2(nsIServiceManager** aResul
   }
 
   sInitialized = true;
 
   mozPoisonValueInit();
 
   NS_LogInit();
 
+  mozilla::LogModule::Init();
+
   JS_SetCurrentEmbedderTimeFunction(TimeSinceProcessCreation);
 
   char aLocal;
   profiler_init(&aLocal);
   nsresult rv = NS_OK;
 
   // We are not shutting down
   gXPCOMShuttingDown = false;
--- a/xpcom/glue/moz.build
+++ b/xpcom/glue/moz.build
@@ -70,17 +70,16 @@ EXPORTS.mozilla += [
     'AutoRestore.h',
     'BlockingResourceBase.h',
     'CondVar.h',
     'DeadlockDetector.h',
     'EnumeratedArrayCycleCollection.h',
     'FileUtils.h',
     'GenericFactory.h',
     'IntentionalCrash.h',
-    'Logging.h',
     'Monitor.h',
     'Mutex.h',
     'Observer.h',
     'ReentrantMonitor.h',
 ]
 
 include('objs.mozbuild')