LateWriteChecks.cpp 7.1 KB

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