Coverage Report

Created: 2018-09-25 14:53

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