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