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 "CrashReporterHost.h"
8 : #include "CrashReporterMetadataShmem.h"
9 : #include "mozilla/ipc/GeckoChildProcessHost.h"
10 : #include "mozilla/Sprintf.h"
11 : #include "mozilla/SyncRunnable.h"
12 : #include "mozilla/Telemetry.h"
13 : #ifdef MOZ_CRASHREPORTER
14 : # include "nsExceptionHandler.h"
15 : # include "nsIAsyncShutdown.h"
16 : # include "nsICrashService.h"
17 : #endif
18 :
19 : namespace mozilla {
20 : namespace ipc {
21 :
22 1 : CrashReporterHost::CrashReporterHost(GeckoProcessType aProcessType,
23 : const Shmem& aShmem,
24 1 : ThreadId aThreadId)
25 : : mProcessType(aProcessType),
26 : mShmem(aShmem),
27 : mThreadId(aThreadId),
28 1 : mStartTime(::time(nullptr)),
29 2 : mFinalized(false)
30 : {
31 1 : }
32 :
33 : #ifdef MOZ_CRASHREPORTER
34 : bool
35 0 : CrashReporterHost::GenerateCrashReport(base::ProcessId aPid)
36 : {
37 0 : if (!TakeCrashedChildMinidump(aPid, nullptr)) {
38 0 : return false;
39 : }
40 0 : return FinalizeCrashReport();
41 : }
42 :
43 : RefPtr<nsIFile>
44 0 : CrashReporterHost::TakeCrashedChildMinidump(base::ProcessId aPid, uint32_t* aOutSequence)
45 : {
46 0 : MOZ_ASSERT(!HasMinidump());
47 :
48 0 : RefPtr<nsIFile> crashDump;
49 0 : if (!XRE_TakeMinidumpForChild(aPid, getter_AddRefs(crashDump), aOutSequence)) {
50 0 : return nullptr;
51 : }
52 0 : if (!AdoptMinidump(crashDump)) {
53 0 : return nullptr;
54 : }
55 0 : return crashDump.get();
56 : }
57 :
58 : bool
59 0 : CrashReporterHost::AdoptMinidump(nsIFile* aFile)
60 : {
61 0 : return CrashReporter::GetIDFromMinidump(aFile, mDumpID);
62 : }
63 :
64 : bool
65 0 : CrashReporterHost::FinalizeCrashReport()
66 : {
67 0 : MOZ_ASSERT(!mFinalized);
68 0 : MOZ_ASSERT(HasMinidump());
69 :
70 0 : CrashReporter::AnnotationTable notes;
71 :
72 0 : nsAutoCString type;
73 0 : switch (mProcessType) {
74 : case GeckoProcessType_Content:
75 0 : type = NS_LITERAL_CSTRING("content");
76 0 : break;
77 : case GeckoProcessType_Plugin:
78 : case GeckoProcessType_GMPlugin:
79 0 : type = NS_LITERAL_CSTRING("plugin");
80 0 : break;
81 : case GeckoProcessType_GPU:
82 0 : type = NS_LITERAL_CSTRING("gpu");
83 0 : break;
84 : default:
85 0 : NS_ERROR("unknown process type");
86 0 : break;
87 : }
88 0 : notes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
89 :
90 : char startTime[32];
91 0 : SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime));
92 0 : notes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
93 :
94 : // We might not have shmem (for example, when running crashreporter tests).
95 0 : if (mShmem.IsReadable()) {
96 0 : CrashReporterMetadataShmem::ReadAppNotes(mShmem, ¬es);
97 : }
98 0 : CrashReporter::AppendExtraData(mDumpID, mExtraNotes);
99 0 : CrashReporter::AppendExtraData(mDumpID, notes);
100 :
101 : // Use mExtraNotes, since NotifyCrashService looks for "PluginHang" which is
102 : // set in the parent process.
103 0 : NotifyCrashService(mProcessType, mDumpID, &mExtraNotes);
104 :
105 0 : mFinalized = true;
106 0 : return true;
107 : }
108 :
109 : namespace {
110 : class GenerateMinidumpShutdownBlocker : public nsIAsyncShutdownBlocker {
111 : public:
112 0 : GenerateMinidumpShutdownBlocker() = default;
113 :
114 0 : NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aBarrierClient) override
115 : {
116 0 : return NS_OK;
117 : }
118 :
119 0 : NS_IMETHOD GetName(nsAString& aName) override
120 : {
121 0 : aName = NS_LITERAL_STRING("Crash Reporter: blocking on minidump"
122 0 : "generation.");
123 0 : return NS_OK;
124 : }
125 :
126 0 : NS_IMETHOD GetState(nsIPropertyBag**) override
127 : {
128 0 : return NS_OK;
129 : }
130 :
131 : NS_DECL_THREADSAFE_ISUPPORTS
132 :
133 : private:
134 0 : virtual ~GenerateMinidumpShutdownBlocker() = default;
135 : };
136 :
137 0 : NS_IMPL_ISUPPORTS(GenerateMinidumpShutdownBlocker, nsIAsyncShutdownBlocker)
138 : }
139 :
140 0 : static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier()
141 : {
142 0 : MOZ_ASSERT(NS_IsMainThread());
143 :
144 0 : nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
145 0 : nsCOMPtr<nsIAsyncShutdownClient> barrier;
146 0 : nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
147 :
148 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
149 0 : return nullptr;
150 : }
151 :
152 0 : return barrier.forget();
153 : }
154 :
155 : void
156 0 : CrashReporterHost::GenerateMinidumpAndPair(GeckoChildProcessHost* aChildProcess,
157 : nsIFile* aMinidumpToPair,
158 : const nsACString& aPairName,
159 : std::function<void(bool)>&& aCallback,
160 : bool aAsync)
161 : {
162 : base::ProcessHandle childHandle;
163 : #ifdef XP_MACOSX
164 : childHandle = aChildProcess->GetChildTask();
165 : #else
166 0 : childHandle = aChildProcess->GetChildProcessHandle();
167 : #endif
168 :
169 0 : if (!mCreateMinidumpCallback.IsEmpty()) {
170 0 : aCallback(false);
171 0 : return;
172 : }
173 0 : mCreateMinidumpCallback.Init(Move(aCallback), aAsync);
174 :
175 0 : if (!childHandle) {
176 0 : NS_WARNING("Failed to get child process handle.");
177 0 : mCreateMinidumpCallback.Invoke(false);
178 0 : return;
179 : }
180 :
181 0 : nsCOMPtr<nsIAsyncShutdownBlocker> shutdownBlocker;
182 0 : if (aAsync && NS_IsMainThread()) {
183 0 : nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
184 0 : if (!barrier) {
185 0 : mCreateMinidumpCallback.Invoke(false);
186 0 : return;
187 : }
188 :
189 0 : shutdownBlocker = new GenerateMinidumpShutdownBlocker();
190 :
191 0 : nsresult rv = barrier->AddBlocker(shutdownBlocker,
192 0 : NS_LITERAL_STRING(__FILE__), __LINE__,
193 0 : NS_LITERAL_STRING("Minidump generation"));
194 0 : Unused << NS_WARN_IF(NS_FAILED(rv));
195 : }
196 :
197 : std::function<void(bool)> callback =
198 0 : [this, shutdownBlocker](bool aResult) {
199 0 : if (aResult &&
200 0 : CrashReporter::GetIDFromMinidump(this->mTargetDump, this->mDumpID)) {
201 0 : this->mCreateMinidumpCallback.Invoke(true);
202 : } else {
203 0 : this->mCreateMinidumpCallback.Invoke(false);
204 : }
205 :
206 0 : if (shutdownBlocker) {
207 0 : nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
208 0 : if (barrier) {
209 0 : barrier->RemoveBlocker(shutdownBlocker);
210 : }
211 : }
212 0 : };
213 :
214 0 : CrashReporter::CreateMinidumpsAndPair(childHandle,
215 : mThreadId,
216 : aPairName,
217 : aMinidumpToPair,
218 0 : getter_AddRefs(mTargetDump),
219 0 : Move(callback),
220 0 : aAsync);
221 : }
222 :
223 : /* static */ void
224 0 : CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
225 : const nsString& aChildDumpID,
226 : const AnnotationTable* aNotes)
227 : {
228 0 : if (!NS_IsMainThread()) {
229 0 : RefPtr<Runnable> runnable = NS_NewRunnableFunction(
230 0 : "ipc::CrashReporterHost::NotifyCrashService", [=]() -> void {
231 0 : CrashReporterHost::NotifyCrashService(
232 0 : aProcessType, aChildDumpID, aNotes);
233 0 : });
234 0 : RefPtr<nsIThread> mainThread = do_GetMainThread();
235 0 : SyncRunnable::DispatchToThread(mainThread, runnable);
236 0 : return;
237 : }
238 :
239 0 : MOZ_ASSERT(!aChildDumpID.IsEmpty());
240 :
241 : nsCOMPtr<nsICrashService> crashService =
242 0 : do_GetService("@mozilla.org/crashservice;1");
243 0 : if (!crashService) {
244 0 : return;
245 : }
246 :
247 : int32_t processType;
248 0 : int32_t crashType = nsICrashService::CRASH_TYPE_CRASH;
249 :
250 0 : nsCString telemetryKey;
251 :
252 0 : switch (aProcessType) {
253 : case GeckoProcessType_Content:
254 0 : processType = nsICrashService::PROCESS_TYPE_CONTENT;
255 0 : telemetryKey.AssignLiteral("content");
256 0 : break;
257 : case GeckoProcessType_Plugin: {
258 0 : processType = nsICrashService::PROCESS_TYPE_PLUGIN;
259 0 : telemetryKey.AssignLiteral("plugin");
260 0 : nsAutoCString val;
261 0 : if (aNotes->Get(NS_LITERAL_CSTRING("PluginHang"), &val) &&
262 0 : val.Equals(NS_LITERAL_CSTRING("1"))) {
263 0 : crashType = nsICrashService::CRASH_TYPE_HANG;
264 0 : telemetryKey.AssignLiteral("pluginhang");
265 : }
266 0 : break;
267 : }
268 : case GeckoProcessType_GMPlugin:
269 0 : processType = nsICrashService::PROCESS_TYPE_GMPLUGIN;
270 0 : telemetryKey.AssignLiteral("gmplugin");
271 0 : break;
272 : case GeckoProcessType_GPU:
273 0 : processType = nsICrashService::PROCESS_TYPE_GPU;
274 0 : telemetryKey.AssignLiteral("gpu");
275 0 : break;
276 : default:
277 0 : NS_ERROR("unknown process type");
278 0 : return;
279 : }
280 :
281 0 : nsCOMPtr<nsISupports> promise;
282 0 : crashService->AddCrash(processType, crashType, aChildDumpID, getter_AddRefs(promise));
283 0 : Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
284 : }
285 :
286 : void
287 0 : CrashReporterHost::AddNote(const nsCString& aKey, const nsCString& aValue)
288 : {
289 0 : mExtraNotes.Put(aKey, aValue);
290 0 : }
291 : #endif
292 :
293 : } // namespace ipc
294 : } // namespace mozilla
|