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