xpcom/build/LateWriteChecks.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/build/LateWriteChecks.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,254 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim:set ts=4 sw=4 sts=4 ci et: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include <algorithm>
    1.11 +
    1.12 +#include "mozilla/IOInterposer.h"
    1.13 +#include "mozilla/PoisonIOInterposer.h"
    1.14 +#include "mozilla/ProcessedStack.h"
    1.15 +#include "mozilla/SHA1.h"
    1.16 +#include "mozilla/Scoped.h"
    1.17 +#include "mozilla/StaticPtr.h"
    1.18 +#include "mozilla/Telemetry.h"
    1.19 +#include "nsAppDirectoryServiceDefs.h"
    1.20 +#include "nsDirectoryServiceUtils.h"
    1.21 +#include "nsPrintfCString.h"
    1.22 +#include "nsStackWalk.h"
    1.23 +#include "plstr.h"
    1.24 +
    1.25 +#ifdef XP_WIN
    1.26 +#define NS_T(str) L ## str
    1.27 +#define NS_SLASH "\\"
    1.28 +#include <fcntl.h>
    1.29 +#include <io.h>
    1.30 +#include <stdio.h>
    1.31 +#include <stdlib.h>
    1.32 +#include <sys/stat.h>
    1.33 +#include <windows.h>
    1.34 +#else
    1.35 +#define NS_SLASH "/"
    1.36 +#endif
    1.37 +
    1.38 +#include "LateWriteChecks.h"
    1.39 +
    1.40 +#if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG))
    1.41 +#define OBSERVE_LATE_WRITES
    1.42 +#endif
    1.43 +
    1.44 +using namespace mozilla;
    1.45 +
    1.46 +/*************************** Auxiliary Declarations ***************************/
    1.47 +
    1.48 +// This a wrapper over a file descriptor that provides a Printf method and
    1.49 +// computes the sha1 of the data that passes through it.
    1.50 +class SHA1Stream
    1.51 +{
    1.52 +public:
    1.53 +    explicit SHA1Stream(FILE *stream)
    1.54 +      : mFile(stream)
    1.55 +    {
    1.56 +      MozillaRegisterDebugFILE(mFile);
    1.57 +    }
    1.58 +
    1.59 +    void Printf(const char *aFormat, ...)
    1.60 +    {
    1.61 +        MOZ_ASSERT(mFile);
    1.62 +        va_list list;
    1.63 +        va_start(list, aFormat);
    1.64 +        nsAutoCString str;
    1.65 +        str.AppendPrintf(aFormat, list);
    1.66 +        va_end(list);
    1.67 +        mSHA1.update(str.get(), str.Length());
    1.68 +        fwrite(str.get(), 1, str.Length(), mFile);
    1.69 +    }
    1.70 +    void Finish(SHA1Sum::Hash &aHash)
    1.71 +    {
    1.72 +        int fd = fileno(mFile);
    1.73 +        fflush(mFile);
    1.74 +        MozillaUnRegisterDebugFD(fd);
    1.75 +        fclose(mFile);
    1.76 +        mSHA1.finish(aHash);
    1.77 +        mFile = nullptr;
    1.78 +    }
    1.79 +private:
    1.80 +    FILE *mFile;
    1.81 +    SHA1Sum mSHA1;
    1.82 +};
    1.83 +
    1.84 +static void RecordStackWalker(void *aPC, void *aSP, void *aClosure)
    1.85 +{
    1.86 +    std::vector<uintptr_t> *stack =
    1.87 +        static_cast<std::vector<uintptr_t>*>(aClosure);
    1.88 +    stack->push_back(reinterpret_cast<uintptr_t>(aPC));
    1.89 +}
    1.90 +
    1.91 +/**************************** Late-Write Observer  ****************************/
    1.92 +
    1.93 +/**
    1.94 + * An implementation of IOInterposeObserver to be registered with IOInterposer.
    1.95 + * This observer logs all writes as late writes.
    1.96 + */
    1.97 +class LateWriteObserver MOZ_FINAL : public IOInterposeObserver
    1.98 +{
    1.99 +public:
   1.100 +  LateWriteObserver(const char* aProfileDirectory)
   1.101 +    : mProfileDirectory(PL_strdup(aProfileDirectory))
   1.102 +  {
   1.103 +  }
   1.104 +  ~LateWriteObserver() {
   1.105 +    PL_strfree(mProfileDirectory);
   1.106 +    mProfileDirectory = nullptr;
   1.107 +  }
   1.108 +
   1.109 +  void Observe(IOInterposeObserver::Observation& aObservation);
   1.110 +private:
   1.111 +  char* mProfileDirectory;
   1.112 +};
   1.113 +
   1.114 +void LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb)
   1.115 +{
   1.116 +#ifdef OBSERVE_LATE_WRITES
   1.117 +  // Crash if that is the shutdown check mode
   1.118 +  if (gShutdownChecks == SCM_CRASH) {
   1.119 +    MOZ_CRASH();
   1.120 +  }
   1.121 +
   1.122 +  // If we have shutdown mode SCM_NOTHING or we can't record then abort
   1.123 +  if (gShutdownChecks == SCM_NOTHING || !Telemetry::CanRecord()) {
   1.124 +    return;
   1.125 +  }
   1.126 +
   1.127 +  // Write the stack and loaded libraries to a file. We can get here
   1.128 +  // concurrently from many writes, so we use multiple temporary files.
   1.129 +  std::vector<uintptr_t> rawStack;
   1.130 +
   1.131 +  NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
   1.132 +               reinterpret_cast<void*>(&rawStack), 0, nullptr);
   1.133 +  Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
   1.134 +
   1.135 +  nsPrintfCString nameAux("%s%s%s", mProfileDirectory,
   1.136 +                          NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
   1.137 +  char *name;
   1.138 +  nameAux.GetMutableData(&name);
   1.139 +
   1.140 +  // We want the sha1 of the entire file, so please don't write to fd
   1.141 +  // directly; use sha1Stream.
   1.142 +  FILE *stream;
   1.143 +#ifdef XP_WIN
   1.144 +  HANDLE hFile;
   1.145 +  do {
   1.146 +    // mkstemp isn't supported so keep trying until we get a file
   1.147 +    int result = _mktemp_s(name, strlen(name) + 1);
   1.148 +    hFile = CreateFileA(name, GENERIC_WRITE, 0, nullptr, CREATE_NEW,
   1.149 +                        FILE_ATTRIBUTE_NORMAL, nullptr);
   1.150 +  } while (GetLastError() == ERROR_FILE_EXISTS);
   1.151 +
   1.152 +  if (hFile == INVALID_HANDLE_VALUE) {
   1.153 +    NS_RUNTIMEABORT("Um, how did we get here?");
   1.154 +  }
   1.155 +
   1.156 +  // http://support.microsoft.com/kb/139640
   1.157 +  int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
   1.158 +  if (fd == -1) {
   1.159 +    NS_RUNTIMEABORT("Um, how did we get here?");
   1.160 +  }
   1.161 +
   1.162 +  stream = _fdopen(fd, "w");
   1.163 +#else
   1.164 +  int fd = mkstemp(name);
   1.165 +  stream = fdopen(fd, "w");
   1.166 +#endif
   1.167 +
   1.168 +  SHA1Stream sha1Stream(stream);
   1.169 +
   1.170 +  size_t numModules = stack.GetNumModules();
   1.171 +  sha1Stream.Printf("%u\n", (unsigned)numModules);
   1.172 +  for (size_t i = 0; i < numModules; ++i) {
   1.173 +    Telemetry::ProcessedStack::Module module = stack.GetModule(i);
   1.174 +    sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
   1.175 +                      module.mName.c_str());
   1.176 +  }
   1.177 +
   1.178 +  size_t numFrames = stack.GetStackSize();
   1.179 +  sha1Stream.Printf("%u\n", (unsigned)numFrames);
   1.180 +  for (size_t i = 0; i < numFrames; ++i) {
   1.181 +    const Telemetry::ProcessedStack::Frame &frame =
   1.182 +        stack.GetFrame(i);
   1.183 +    // NOTE: We write the offsets, while the atos tool expects a value with
   1.184 +    // the virtual address added. For example, running otool -l on the the firefox
   1.185 +    // binary shows
   1.186 +    //      cmd LC_SEGMENT_64
   1.187 +    //      cmdsize 632
   1.188 +    //      segname __TEXT
   1.189 +    //      vmaddr 0x0000000100000000
   1.190 +    // so to print the line matching the offset 123 one has to run
   1.191 +    // atos -o firefox 0x100000123.
   1.192 +    sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
   1.193 +  }
   1.194 +
   1.195 +  SHA1Sum::Hash sha1;
   1.196 +  sha1Stream.Finish(sha1);
   1.197 +
   1.198 +  // Note: These files should be deleted by telemetry once it reads them. If
   1.199 +  // there were no telemetry runs by the time we shut down, we just add files
   1.200 +  // to the existing ones instead of replacing them. Given that each of these
   1.201 +  // files is a bug to be fixed, that is probably the right thing to do.
   1.202 +
   1.203 +  // We append the sha1 of the contents to the file name. This provides a simple
   1.204 +  // client side deduplication.
   1.205 +  nsPrintfCString finalName("%s%s", mProfileDirectory,
   1.206 +                            "/Telemetry.LateWriteFinal-");
   1.207 +  for (int i = 0; i < 20; ++i) {
   1.208 +    finalName.AppendPrintf("%02x", sha1[i]);
   1.209 +  }
   1.210 +  PR_Delete(finalName.get());
   1.211 +  PR_Rename(name, finalName.get());
   1.212 +#endif
   1.213 +}
   1.214 +
   1.215 +/******************************* Setup/Teardown *******************************/
   1.216 +
   1.217 +static StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
   1.218 +
   1.219 +namespace mozilla{
   1.220 +
   1.221 +void InitLateWriteChecks()
   1.222 +{
   1.223 +  nsCOMPtr<nsIFile> mozFile;
   1.224 +  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
   1.225 +  if (mozFile) {
   1.226 +    nsAutoCString nativePath;
   1.227 +    nsresult rv = mozFile->GetNativePath(nativePath);
   1.228 +    if (NS_SUCCEEDED(rv) && nativePath.get()) {
   1.229 +      sLateWriteObserver = new LateWriteObserver(nativePath.get());
   1.230 +    }
   1.231 +  }
   1.232 +}
   1.233 +
   1.234 +void BeginLateWriteChecks()
   1.235 +{
   1.236 +  if (sLateWriteObserver) {
   1.237 +    IOInterposer::Register(
   1.238 +      IOInterposeObserver::OpWriteFSync,
   1.239 +      sLateWriteObserver
   1.240 +    );
   1.241 +  }
   1.242 +}
   1.243 +
   1.244 +void StopLateWriteChecks()
   1.245 +{
   1.246 +  if (sLateWriteObserver) {
   1.247 +    IOInterposer::Unregister(
   1.248 +      IOInterposeObserver::OpAll,
   1.249 +      sLateWriteObserver
   1.250 +    );
   1.251 +    // Deallocation would not be thread-safe, and StopLateWriteChecks() is
   1.252 +    // called at shutdown and only in special cases.
   1.253 +    // sLateWriteObserver = nullptr;
   1.254 +  }
   1.255 +}
   1.256 +
   1.257 +} // namespace mozilla

mercurial