Bug 1451255 - make nsTraceRefcnt handle multibyte characters in log files; r=mccr8
authorNathan Froyd <froydnj@mozilla.com>
Tue, 17 Apr 2018 10:13:42 -0400
changeset 414113 7d89a66dea5f2f7f6d71c947b50ee268e20066e8
parent 414112 0d65829a60e72992181f7536d970f05b6245687b
child 414114 f5f4f7d7fcdf6db3d6599e49b42b23dcdba3fe71
push id33858
push userncsoregi@mozilla.com
push dateTue, 17 Apr 2018 21:55:44 +0000
treeherdermozilla-central@d6eb5597d744 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1451255
milestone61.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 1451255 - make nsTraceRefcnt handle multibyte characters in log files; r=mccr8 ...on Windows, at least. Apparently if you have environment variables set that contain multibyte characters, and ask for them with getenv, you get garbage. Or perhaps you get something sensible, but then passing it to fopen produces garbage. Either way, the most reasonable way to handle this is to use the Windows wide-character APIs all over.
xpcom/base/nsTraceRefcnt.cpp
--- a/xpcom/base/nsTraceRefcnt.cpp
+++ b/xpcom/base/nsTraceRefcnt.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "nsTraceRefcnt.h"
 #include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Path.h"
 #include "mozilla/StaticPtr.h"
 #include "nsXPCOMPrivate.h"
 #include "nscore.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "prenv.h"
 #include "plstr.h"
@@ -659,65 +660,78 @@ RecycleSerialNumberPtr(void* aPtr)
 }
 
 static bool
 LogThisObj(intptr_t aSerialNumber)
 {
   return (bool)PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber);
 }
 
-#ifdef XP_WIN
-#define FOPEN_NO_INHERIT "N"
-#else
-#define FOPEN_NO_INHERIT
-#endif
+using EnvCharType = mozilla::filesystem::Path::value_type;
 
 static bool
-InitLog(const char* aEnvVar, const char* aMsg, FILE** aResult)
+InitLog(const EnvCharType* aEnvVar, const char* aMsg, FILE** aResult)
 {
-  const char* value = getenv(aEnvVar);
+#ifdef XP_WIN
+  // This is gross, I know.
+  const wchar_t* envvar = reinterpret_cast<const wchar_t*>(aEnvVar);
+  const char16_t* value = reinterpret_cast<const char16_t*>(::_wgetenv(envvar));
+#define ENVVAR_PRINTF "%S"
+#else
+  const char* envvar = aEnvVar;
+  const char* value = ::getenv(aEnvVar);
+#define ENVVAR_PRINTF "%s"
+#endif
+
   if (value) {
-    if (nsCRT::strcmp(value, "1") == 0) {
+    nsTDependentString<EnvCharType> fname(value);
+    if (fname.EqualsLiteral("1")) {
       *aResult = stdout;
-      fprintf(stdout, "### %s defined -- logging %s to stdout\n",
-              aEnvVar, aMsg);
+      fprintf(stdout, "### " ENVVAR_PRINTF " defined -- logging %s to stdout\n",
+              envvar, aMsg);
       return true;
-    } else if (nsCRT::strcmp(value, "2") == 0) {
+    } else if (fname.EqualsLiteral("2")) {
       *aResult = stderr;
-      fprintf(stdout, "### %s defined -- logging %s to stderr\n",
-              aEnvVar, aMsg);
+      fprintf(stdout, "### " ENVVAR_PRINTF " defined -- logging %s to stderr\n",
+              envvar, aMsg);
       return true;
     } else {
-      FILE* stream;
-      nsAutoCString fname(value);
       if (!XRE_IsParentProcess()) {
         bool hasLogExtension =
           fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
         if (hasLogExtension) {
           fname.Cut(fname.Length() - 4, 4);
         }
         fname.Append('_');
-        fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
+        const char* processType = XRE_ChildProcessTypeToString(XRE_GetProcessType());
+        fname.AppendASCII(processType);
         fname.AppendLiteral("_pid");
         fname.AppendInt((uint32_t)getpid());
         if (hasLogExtension) {
           fname.AppendLiteral(".log");
         }
       }
-      stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT);
+#ifdef XP_WIN
+      FILE* stream = ::_wfopen(fname.get(), L"wN");
+      const wchar_t* fp = (const wchar_t*)fname.get();
+#else
+      FILE* stream = ::fopen(fname.get(), "w");
+      const char* fp = fname.get();
+#endif
       if (stream) {
         MozillaRegisterDebugFD(fileno(stream));
         *aResult = stream;
-        fprintf(stderr, "### %s defined -- logging %s to %s\n",
-                aEnvVar, aMsg, fname.get());
+        fprintf(stderr, "### " ENVVAR_PRINTF " defined -- logging %s to " ENVVAR_PRINTF "\n",
+                envvar, aMsg, fp);
       } else {
-        fprintf(stderr, "### %s defined -- unable to log %s to %s\n",
-                aEnvVar, aMsg, fname.get());
+        fprintf(stderr, "### " ENVVAR_PRINTF " defined -- unable to log %s to " ENVVAR_PRINTF "\n",
+                envvar, aMsg, fp);
         MOZ_ASSERT(false, "Tried and failed to create an XPCOM log");
       }
+#undef ENVVAR_PRINTF
       return stream != nullptr;
     }
   }
   return false;
 }
 
 
 static void
@@ -731,55 +745,63 @@ maybeUnregisterAndCloseFile(FILE*& aFile
   fclose(aFile);
   aFile = nullptr;
 }
 
 
 static void
 InitTraceLog()
 {
+#ifdef XP_WIN
+#define ENVVAR(x) u"" x
+#else
+#define ENVVAR(x) x
+#endif
+
   if (gInitialized) {
     return;
   }
   gInitialized = true;
 
-  bool defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
+  bool defined = InitLog(ENVVAR("XPCOM_MEM_BLOAT_LOG"), "bloat/leaks", &gBloatLog);
   if (!defined) {
-    gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
+    gLogLeaksOnly = InitLog(ENVVAR("XPCOM_MEM_LEAK_LOG"), "leaks", &gBloatLog);
   }
   if (defined || gLogLeaksOnly) {
     RecreateBloatView();
     if (!gBloatView) {
       NS_WARNING("out of memory");
       maybeUnregisterAndCloseFile(gBloatLog);
       gLogLeaksOnly = false;
     }
   }
 
-  InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
+  InitLog(ENVVAR("XPCOM_MEM_REFCNT_LOG"), "refcounts", &gRefcntsLog);
 
-  InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
+  InitLog(ENVVAR("XPCOM_MEM_ALLOC_LOG"), "new/delete", &gAllocLog);
 
   const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
 
 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
   if (classes) {
-    InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
+    InitLog(ENVVAR("XPCOM_MEM_COMPTR_LOG"), "nsCOMPtr", &gCOMPtrLog);
   } else {
     if (getenv("XPCOM_MEM_COMPTR_LOG")) {
       fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
     }
   }
 #else
   const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
   if (comptr_log) {
     fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
   }
 #endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
 
+#undef ENVVAR
+
   if (classes) {
     // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
     // as a list of class names to track
     gTypesToLog = PL_NewHashTable(256,
                                   PL_HashString,
                                   PL_CompareStrings,
                                   PL_CompareValues,
                                   &typesToLogHashAllocOps, nullptr);