xpcom/build/LateWriteChecks.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:95664751bd6d
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/. */
6
7 #include <algorithm>
8
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"
21
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
34
35 #include "LateWriteChecks.h"
36
37 #if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG))
38 #define OBSERVE_LATE_WRITES
39 #endif
40
41 using namespace mozilla;
42
43 /*************************** Auxiliary Declarations ***************************/
44
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 }
55
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 };
80
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 }
87
88 /**************************** Late-Write Observer ****************************/
89
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 }
105
106 void Observe(IOInterposeObserver::Observation& aObservation);
107 private:
108 char* mProfileDirectory;
109 };
110
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 }
118
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 }
123
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;
127
128 NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
129 reinterpret_cast<void*>(&rawStack), 0, nullptr);
130 Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
131
132 nsPrintfCString nameAux("%s%s%s", mProfileDirectory,
133 NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
134 char *name;
135 nameAux.GetMutableData(&name);
136
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);
148
149 if (hFile == INVALID_HANDLE_VALUE) {
150 NS_RUNTIMEABORT("Um, how did we get here?");
151 }
152
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 }
158
159 stream = _fdopen(fd, "w");
160 #else
161 int fd = mkstemp(name);
162 stream = fdopen(fd, "w");
163 #endif
164
165 SHA1Stream sha1Stream(stream);
166
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 }
174
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 }
191
192 SHA1Sum::Hash sha1;
193 sha1Stream.Finish(sha1);
194
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.
199
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 }
211
212 /******************************* Setup/Teardown *******************************/
213
214 static StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
215
216 namespace mozilla{
217
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 }
230
231 void BeginLateWriteChecks()
232 {
233 if (sLateWriteObserver) {
234 IOInterposer::Register(
235 IOInterposeObserver::OpWriteFSync,
236 sLateWriteObserver
237 );
238 }
239 }
240
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 }
253
254 } // namespace mozilla

mercurial