xpcom/build/LateWriteChecks.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set ts=4 sw=4 sts=4 ci et: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <algorithm>
     9 #include "mozilla/IOInterposer.h"
    10 #include "mozilla/PoisonIOInterposer.h"
    11 #include "mozilla/ProcessedStack.h"
    12 #include "mozilla/SHA1.h"
    13 #include "mozilla/Scoped.h"
    14 #include "mozilla/StaticPtr.h"
    15 #include "mozilla/Telemetry.h"
    16 #include "nsAppDirectoryServiceDefs.h"
    17 #include "nsDirectoryServiceUtils.h"
    18 #include "nsPrintfCString.h"
    19 #include "nsStackWalk.h"
    20 #include "plstr.h"
    22 #ifdef XP_WIN
    23 #define NS_T(str) L ## str
    24 #define NS_SLASH "\\"
    25 #include <fcntl.h>
    26 #include <io.h>
    27 #include <stdio.h>
    28 #include <stdlib.h>
    29 #include <sys/stat.h>
    30 #include <windows.h>
    31 #else
    32 #define NS_SLASH "/"
    33 #endif
    35 #include "LateWriteChecks.h"
    37 #if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG))
    38 #define OBSERVE_LATE_WRITES
    39 #endif
    41 using namespace mozilla;
    43 /*************************** Auxiliary Declarations ***************************/
    45 // This a wrapper over a file descriptor that provides a Printf method and
    46 // computes the sha1 of the data that passes through it.
    47 class SHA1Stream
    48 {
    49 public:
    50     explicit SHA1Stream(FILE *stream)
    51       : mFile(stream)
    52     {
    53       MozillaRegisterDebugFILE(mFile);
    54     }
    56     void Printf(const char *aFormat, ...)
    57     {
    58         MOZ_ASSERT(mFile);
    59         va_list list;
    60         va_start(list, aFormat);
    61         nsAutoCString str;
    62         str.AppendPrintf(aFormat, list);
    63         va_end(list);
    64         mSHA1.update(str.get(), str.Length());
    65         fwrite(str.get(), 1, str.Length(), mFile);
    66     }
    67     void Finish(SHA1Sum::Hash &aHash)
    68     {
    69         int fd = fileno(mFile);
    70         fflush(mFile);
    71         MozillaUnRegisterDebugFD(fd);
    72         fclose(mFile);
    73         mSHA1.finish(aHash);
    74         mFile = nullptr;
    75     }
    76 private:
    77     FILE *mFile;
    78     SHA1Sum mSHA1;
    79 };
    81 static void RecordStackWalker(void *aPC, void *aSP, void *aClosure)
    82 {
    83     std::vector<uintptr_t> *stack =
    84         static_cast<std::vector<uintptr_t>*>(aClosure);
    85     stack->push_back(reinterpret_cast<uintptr_t>(aPC));
    86 }
    88 /**************************** Late-Write Observer  ****************************/
    90 /**
    91  * An implementation of IOInterposeObserver to be registered with IOInterposer.
    92  * This observer logs all writes as late writes.
    93  */
    94 class LateWriteObserver MOZ_FINAL : public IOInterposeObserver
    95 {
    96 public:
    97   LateWriteObserver(const char* aProfileDirectory)
    98     : mProfileDirectory(PL_strdup(aProfileDirectory))
    99   {
   100   }
   101   ~LateWriteObserver() {
   102     PL_strfree(mProfileDirectory);
   103     mProfileDirectory = nullptr;
   104   }
   106   void Observe(IOInterposeObserver::Observation& aObservation);
   107 private:
   108   char* mProfileDirectory;
   109 };
   111 void LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb)
   112 {
   113 #ifdef OBSERVE_LATE_WRITES
   114   // Crash if that is the shutdown check mode
   115   if (gShutdownChecks == SCM_CRASH) {
   116     MOZ_CRASH();
   117   }
   119   // If we have shutdown mode SCM_NOTHING or we can't record then abort
   120   if (gShutdownChecks == SCM_NOTHING || !Telemetry::CanRecord()) {
   121     return;
   122   }
   124   // Write the stack and loaded libraries to a file. We can get here
   125   // concurrently from many writes, so we use multiple temporary files.
   126   std::vector<uintptr_t> rawStack;
   128   NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
   129                reinterpret_cast<void*>(&rawStack), 0, nullptr);
   130   Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
   132   nsPrintfCString nameAux("%s%s%s", mProfileDirectory,
   133                           NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
   134   char *name;
   135   nameAux.GetMutableData(&name);
   137   // We want the sha1 of the entire file, so please don't write to fd
   138   // directly; use sha1Stream.
   139   FILE *stream;
   140 #ifdef XP_WIN
   141   HANDLE hFile;
   142   do {
   143     // mkstemp isn't supported so keep trying until we get a file
   144     int result = _mktemp_s(name, strlen(name) + 1);
   145     hFile = CreateFileA(name, GENERIC_WRITE, 0, nullptr, CREATE_NEW,
   146                         FILE_ATTRIBUTE_NORMAL, nullptr);
   147   } while (GetLastError() == ERROR_FILE_EXISTS);
   149   if (hFile == INVALID_HANDLE_VALUE) {
   150     NS_RUNTIMEABORT("Um, how did we get here?");
   151   }
   153   // http://support.microsoft.com/kb/139640
   154   int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
   155   if (fd == -1) {
   156     NS_RUNTIMEABORT("Um, how did we get here?");
   157   }
   159   stream = _fdopen(fd, "w");
   160 #else
   161   int fd = mkstemp(name);
   162   stream = fdopen(fd, "w");
   163 #endif
   165   SHA1Stream sha1Stream(stream);
   167   size_t numModules = stack.GetNumModules();
   168   sha1Stream.Printf("%u\n", (unsigned)numModules);
   169   for (size_t i = 0; i < numModules; ++i) {
   170     Telemetry::ProcessedStack::Module module = stack.GetModule(i);
   171     sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
   172                       module.mName.c_str());
   173   }
   175   size_t numFrames = stack.GetStackSize();
   176   sha1Stream.Printf("%u\n", (unsigned)numFrames);
   177   for (size_t i = 0; i < numFrames; ++i) {
   178     const Telemetry::ProcessedStack::Frame &frame =
   179         stack.GetFrame(i);
   180     // NOTE: We write the offsets, while the atos tool expects a value with
   181     // the virtual address added. For example, running otool -l on the the firefox
   182     // binary shows
   183     //      cmd LC_SEGMENT_64
   184     //      cmdsize 632
   185     //      segname __TEXT
   186     //      vmaddr 0x0000000100000000
   187     // so to print the line matching the offset 123 one has to run
   188     // atos -o firefox 0x100000123.
   189     sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
   190   }
   192   SHA1Sum::Hash sha1;
   193   sha1Stream.Finish(sha1);
   195   // Note: These files should be deleted by telemetry once it reads them. If
   196   // there were no telemetry runs by the time we shut down, we just add files
   197   // to the existing ones instead of replacing them. Given that each of these
   198   // files is a bug to be fixed, that is probably the right thing to do.
   200   // We append the sha1 of the contents to the file name. This provides a simple
   201   // client side deduplication.
   202   nsPrintfCString finalName("%s%s", mProfileDirectory,
   203                             "/Telemetry.LateWriteFinal-");
   204   for (int i = 0; i < 20; ++i) {
   205     finalName.AppendPrintf("%02x", sha1[i]);
   206   }
   207   PR_Delete(finalName.get());
   208   PR_Rename(name, finalName.get());
   209 #endif
   210 }
   212 /******************************* Setup/Teardown *******************************/
   214 static StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
   216 namespace mozilla{
   218 void InitLateWriteChecks()
   219 {
   220   nsCOMPtr<nsIFile> mozFile;
   221   NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
   222   if (mozFile) {
   223     nsAutoCString nativePath;
   224     nsresult rv = mozFile->GetNativePath(nativePath);
   225     if (NS_SUCCEEDED(rv) && nativePath.get()) {
   226       sLateWriteObserver = new LateWriteObserver(nativePath.get());
   227     }
   228   }
   229 }
   231 void BeginLateWriteChecks()
   232 {
   233   if (sLateWriteObserver) {
   234     IOInterposer::Register(
   235       IOInterposeObserver::OpWriteFSync,
   236       sLateWriteObserver
   237     );
   238   }
   239 }
   241 void StopLateWriteChecks()
   242 {
   243   if (sLateWriteObserver) {
   244     IOInterposer::Unregister(
   245       IOInterposeObserver::OpAll,
   246       sLateWriteObserver
   247     );
   248     // Deallocation would not be thread-safe, and StopLateWriteChecks() is
   249     // called at shutdown and only in special cases.
   250     // sLateWriteObserver = nullptr;
   251   }
   252 }
   254 } // namespace mozilla

mercurial