Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(365)

Side by Side Diff: goopdate/crash.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « goopdate/crash.h ('k') | goopdate/crash_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2007-2010 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 // ========================================================================
15 //
16 // TODO(omaha): for reliability sake, the code that sets up the exception
17 // handler should be very minimalist and not call so much outside of this
18 // module. One idea is to split the crash module in two: one minimalist part
19 // responsible for setting up the exception handler and one that is uploading
20 // the crash.
21
22 #include "omaha/goopdate/crash.h"
23
24 #include <windows.h>
25 #include <shlwapi.h>
26 #include <atlbase.h>
27 #include <atlstr.h>
28 #include <cmath>
29 #include <map>
30 #include <string>
31 #include <vector>
32 #include "base/basictypes.h"
33 #include "omaha/base/app_util.h"
34 #include "omaha/base/const_addresses.h"
35 #include "omaha/base/const_object_names.h"
36 #include "omaha/base/constants.h"
37 #include "omaha/base/debug.h"
38 #include "omaha/base/error.h"
39 #include "omaha/base/exception_barrier.h"
40 #include "omaha/base/file.h"
41 #include "omaha/base/logging.h"
42 #include "omaha/base/module_utils.h"
43 #include "omaha/base/omaha_version.h"
44 #include "omaha/base/path.h"
45 #include "omaha/base/reg_key.h"
46 #include "omaha/base/safe_format.h"
47 #include "omaha/base/scope_guard.h"
48 #include "omaha/base/scoped_any.h"
49 #include "omaha/base/time.h"
50 #include "omaha/base/utils.h"
51 #include "omaha/base/vistautil.h"
52 #include "omaha/common/command_line_builder.h"
53 #include "omaha/common/config_manager.h"
54 #include "omaha/common/event_logger.h"
55 #include "omaha/common/goopdate_utils.h"
56 #include "omaha/common/stats_uploader.h"
57 #include "omaha/goopdate/goopdate_metrics.h"
58 #include "third_party/breakpad/src/client/windows/common/ipc_protocol.h"
59 #include "third_party/breakpad/src/client/windows/crash_generation/client_info.h "
60 #include "third_party/breakpad/src/client/windows/crash_generation/crash_generat ion_server.h"
61 #include "third_party/breakpad/src/client/windows/sender/crash_report_sender.h"
62
63 using google_breakpad::ClientInfo;
64 using google_breakpad::CrashGenerationServer;
65 using google_breakpad::CrashReportSender;
66 using google_breakpad::CustomClientInfo;
67 using google_breakpad::ExceptionHandler;
68 using google_breakpad::ReportResult;
69
70 namespace omaha {
71
72 const ACCESS_MASK kPipeAccessMask = FILE_READ_ATTRIBUTES |
73 FILE_READ_DATA |
74 FILE_WRITE_ATTRIBUTES |
75 FILE_WRITE_DATA |
76 SYNCHRONIZE;
77
78 CString Crash::module_filename_;
79 CString Crash::crash_dir_;
80 CString Crash::checkpoint_file_;
81 CString Crash::version_postfix_ = kCrashVersionPostfixString;
82 CString Crash::crash_report_url_ = kUrlCrashReport;
83 int Crash::max_reports_per_day_ = kCrashReportMaxReportsPerDay;
84 ExceptionHandler* Crash::exception_handler_ = NULL;
85 CrashGenerationServer* Crash::crash_server_ = NULL;
86
87 bool Crash::is_machine_ = false;
88 const TCHAR* const Crash::kDefaultProductName =
89 SHORT_COMPANY_NAME _T(" Error Reporting");
90
91 HRESULT Crash::Initialize(bool is_machine) {
92 is_machine_ = is_machine;
93
94 HRESULT hr = GetModuleFileName(NULL, &module_filename_);
95 if (FAILED(hr)) {
96 return hr;
97 }
98
99 hr = InitializeCrashDir();
100 if (FAILED(hr)) {
101 return hr;
102 }
103 ASSERT1(!crash_dir_.IsEmpty());
104 CORE_LOG(L2, (_T("[crash dir %s]"), crash_dir_));
105
106 // The checkpoint file maintains state information for the crash report
107 // client, such as the number of reports per day successfully sent.
108 checkpoint_file_ = ConcatenatePath(crash_dir_, _T("checkpoint"));
109 if (checkpoint_file_.IsEmpty()) {
110 return GOOPDATE_E_PATH_APPEND_FAILED;
111 }
112
113 return S_OK;
114 }
115
116 HRESULT Crash::InstallCrashHandler(bool is_machine) {
117 CORE_LOG(L3, (_T("[Crash::InstallCrashHandler][is_machine %d]"), is_machine));
118
119 HRESULT hr = Initialize(is_machine);
120 if (FAILED(hr)) {
121 CORE_LOG(LE, (_T("[failed to initialize Crash][0x%08x]"), hr));
122 return hr;
123 }
124
125 // Only installs the exception handler if the process is not a crash report
126 // process. If the crash reporter crashes as well, this results in an
127 // infinite loop.
128 bool is_crash_report_process = false;
129 if (FAILED(IsCrashReportProcess(&is_crash_report_process)) ||
130 is_crash_report_process) {
131 return S_OK;
132 }
133
134 // Allocate this instance dynamically so that it is going to be
135 // around until the process terminates. Technically, this instance "leaks",
136 // but this is actually the correct behavior.
137 if (exception_handler_) {
138 delete exception_handler_;
139 }
140
141 MINIDUMP_TYPE dump_type = ConfigManager::Instance()->IsInternalUser() ?
142 MiniDumpWithFullMemory : MiniDumpNormal;
143 exception_handler_ = new ExceptionHandler(crash_dir_.GetString(),
144 NULL,
145 &Crash::MinidumpCallback,
146 NULL,
147 ExceptionHandler::HANDLER_ALL,
148 dump_type,
149 NULL,
150 NULL);
151
152 // Breakpad does not get the exceptions that are not propagated to the
153 // UnhandledExceptionFilter. This is the case where we crashed on a stack
154 // which we do not own, such as an RPC stack. To get these exceptions we
155 // initialize a static ExceptionBarrier object.
156 ExceptionBarrier::set_handler(&Crash::EBHandler);
157
158 CORE_LOG(L2, (_T("[exception handler has been installed]")));
159 return S_OK;
160 }
161
162 void Crash::UninstallCrashHandler() {
163 ExceptionBarrier::set_handler(NULL);
164 delete exception_handler_;
165 exception_handler_ = NULL;
166
167 CORE_LOG(L2, (_T("[exception handler has been uninstalled]")));
168 }
169
170 HRESULT Crash::CrashHandler(bool is_machine,
171 const google_breakpad::ClientInfo& client_info,
172 const CString& crash_filename) {
173 // GoogleCrashHandler.exe is only aggregating metrics at process exit. Since
174 // GoogleCrashHandler.exe is long-running however, we hardly ever exit. As a
175 // consequence, the metrics below will be reported very infrequently. This
176 // call below will do additional aggregation of metrics, so that we can report
177 // the metrics below in a timely manner.
178 ON_SCOPE_EXIT(AggregateMetrics, is_machine);
179
180 // Count the number of crashes requested by applications.
181 ++metric_oop_crashes_requested;
182
183 DWORD pid = client_info.pid();
184 OPT_LOG(L1, (_T("[client requested dump][pid %d]"), pid));
185
186 ASSERT1(!crash_filename.IsEmpty());
187 if (crash_filename.IsEmpty()) {
188 OPT_LOG(L1, (_T("[no crash file]")));
189 ++metric_oop_crashes_crash_filename_empty;
190 return E_UNEXPECTED;
191 }
192
193 CString custom_info_filename;
194 HRESULT hr = CreateCustomInfoFile(crash_filename,
195 client_info.GetCustomInfo(),
196 &custom_info_filename);
197 if (FAILED(hr)) {
198 OPT_LOG(LE, (_T("[CreateCustomInfoFile failed][0x%08x]"), hr));
199 ++metric_oop_crashes_createcustominfofile_failed;
200 return hr;
201 }
202
203 // Start a sender process to handle the crash.
204 CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
205 builder.set_crash_filename(crash_filename);
206 builder.set_custom_info_filename(custom_info_filename);
207 builder.set_is_machine_set(is_machine);
208 CString cmd_line = builder.GetCommandLine(module_filename_);
209
210 hr = StartSenderWithCommandLine(&cmd_line);
211 if (FAILED(hr)) {
212 OPT_LOG(LE, (_T("[StartSenderWithCommandLine failed][0x%08x]"), hr));
213 ++metric_oop_crashes_startsenderwithcommandline_failed;
214 return hr;
215 }
216
217 OPT_LOG(L1, (_T("[client dump handled][pid %d]"), pid));
218 return S_OK;
219 }
220
221 // The implementation must be as simple as possible and use only the
222 // resources it needs to start a reporter process and then exit.
223 bool Crash::MinidumpCallback(const wchar_t* dump_path,
224 const wchar_t* minidump_id,
225 void*,
226 EXCEPTION_POINTERS*,
227 MDRawAssertionInfo*,
228 bool succeeded) {
229 if (succeeded && *dump_path && *minidump_id) {
230 // We need a way to see if the crash happens while we are installing
231 // something. This is a tough spot to be doing anything at all since
232 // we've been handling a crash.
233 // TODO(omaha): redesign a better mechanism.
234 bool is_interactive = Crash::IsInteractive();
235
236 // TODO(omaha): format a command line without extra memory allocations.
237 CString crash_filename;
238 SafeCStringFormat(&crash_filename, _T("%s\\%s.dmp"),
239 dump_path, minidump_id);
240 EnclosePath(&crash_filename);
241 StartReportCrash(is_interactive, crash_filename);
242
243 // For in-proc crash generation, ExceptionHandler either creates a Normal
244 // MiniDump, or a Full MiniDump, based on the dump_type. However, in the
245 // case of OOP crash generation both the Normal and Full dumps are created
246 // by the crash handling server, with the default full dump filename having
247 // a suffix of "-full.dmp". If Omaha switches to using OOP crash generation,
248 // this file is uploaded as well.
249 if (ConfigManager::Instance()->IsInternalUser()) {
250 SafeCStringFormat(&crash_filename, _T("%s\\%s-full.dmp"),
251 dump_path, minidump_id);
252 EnclosePath(&crash_filename);
253 if (File::Exists(crash_filename)) {
254 StartReportCrash(is_interactive, crash_filename);
255 }
256 }
257 }
258
259 // There are two ways to stop execution of the current process: ExitProcess
260 // and TerminateProcess. Calling ExitProcess results in calling the
261 // destructors of the static objects before the process exits.
262 // TerminateProcess unconditionally stops the process so no user mode code
263 // executes beyond this point.
264 ::TerminateProcess(::GetCurrentProcess(),
265 static_cast<UINT>(GOOPDATE_E_CRASH));
266 return true;
267 }
268
269 void Crash::StartReportCrash(bool is_interactive,
270 const CString& crash_filename) {
271 // CommandLineBuilder escapes the program name before returning the
272 // command line to the caller.
273 CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
274 builder.set_is_interactive_set(is_interactive);
275 builder.set_crash_filename(crash_filename);
276 builder.set_is_machine_set(is_machine_);
277 CString cmd_line = builder.GetCommandLine(module_filename_);
278
279 // Set an environment variable which the crash reporter process will
280 // inherit. We don't want to install a crash handler for the reporter
281 // process to avoid an infinite loop in the case the reporting process
282 // crashes also. When the reporting process begins execution, the presence
283 // of this environment variable is tested, and the crash handler will not be
284 // installed.
285 if (::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, (_T("1")))) {
286 STARTUPINFO si = {sizeof(si)};
287 PROCESS_INFORMATION pi = {0};
288 if (::CreateProcess(NULL,
289 cmd_line.GetBuffer(),
290 NULL,
291 NULL,
292 false,
293 0,
294 NULL,
295 NULL,
296 &si,
297 &pi)) {
298 ::CloseHandle(pi.hProcess);
299 ::CloseHandle(pi.hThread);
300 }
301 }
302 }
303
304 HRESULT Crash::StartSenderWithCommandLine(CString* cmd_line) {
305 TCHAR* env_vars = ::GetEnvironmentStrings();
306 if (env_vars == NULL) {
307 return HRESULTFromLastError();
308 }
309
310 // Add an environment variable to the crash reporter process to indicate it
311 // not to install a crash handler. This avoids an infinite loop in the case
312 // the reporting process crashes also. When the reporting process begins
313 // execution, the presence of this environment variable is tested, and the
314 // crash handler will not be installed.
315 CString new_var;
316 new_var.Append(kNoCrashHandlerEnvVariableName);
317 new_var.Append(_T("=1"));
318
319 // Compute the length of environment variables string. The format of the
320 // string is Name1=Value1\0Name2=Value2\0Name3=Value3\0\0.
321 const TCHAR* current = env_vars;
322 size_t env_vars_char_count = 0;
323 while (*current) {
324 size_t sub_length = _tcslen(current) + 1;
325 env_vars_char_count += sub_length;
326 current += sub_length;
327 }
328 // Add one to length to count the trailing NULL character marking the end of
329 // all environment variables.
330 ++env_vars_char_count;
331
332 // Copy the new environment variable and the existing variables in a new
333 // buffer.
334 size_t new_var_char_count = new_var.GetLength() + 1;
335 scoped_array<TCHAR> new_env_vars(
336 new TCHAR[env_vars_char_count + new_var_char_count]);
337 size_t new_var_byte_count = new_var_char_count * sizeof(TCHAR);
338 memcpy(new_env_vars.get(),
339 static_cast<const TCHAR*>(new_var),
340 new_var_byte_count);
341 size_t env_vars_byte_count = env_vars_char_count * sizeof(TCHAR);
342 memcpy(new_env_vars.get() + new_var_char_count,
343 env_vars,
344 env_vars_byte_count);
345 ::FreeEnvironmentStrings(env_vars);
346
347 STARTUPINFO si = {sizeof(si)};
348 PROCESS_INFORMATION pi = {0};
349 if (!::CreateProcess(NULL,
350 cmd_line->GetBuffer(),
351 NULL,
352 NULL,
353 false,
354 CREATE_UNICODE_ENVIRONMENT,
355 new_env_vars.get(),
356 NULL,
357 &si,
358 &pi)) {
359 return HRESULTFromLastError();
360 }
361
362 ::CloseHandle(pi.hProcess);
363 ::CloseHandle(pi.hThread);
364 return S_OK;
365 }
366
367 HRESULT Crash::CreateCustomInfoFile(const CString& dump_file,
368 const CustomClientInfo& custom_client_info,
369 CString* custom_info_filepath) {
370 // Since goopdate_utils::WriteNameValuePairsToFile is implemented in terms
371 // of WritePrivateProfile API, relative paths are relative to the Windows
372 // directory instead of the current directory of the process.
373 ASSERT(!::PathIsRelative(dump_file), (_T("Path must be absolute")));
374
375 // Determine the path for custom info file.
376 CString filepath = GetPathRemoveExtension(dump_file);
377 SafeCStringAppendFormat(&filepath, _T(".txt"));
378
379 // Create a map of name/value pairs from custom client info.
380 std::map<CString, CString> custom_info_map;
381 for (size_t i = 0; i < custom_client_info.count; ++i) {
382 custom_info_map[custom_client_info.entries[i].name] =
383 custom_client_info.entries[i].value;
384 }
385
386 HRESULT hr = goopdate_utils::WriteNameValuePairsToFile(filepath,
387 kCustomClientInfoGroup,
388 custom_info_map);
389 if (FAILED(hr)) {
390 return hr;
391 }
392
393 *custom_info_filepath = filepath;
394 return S_OK;
395 }
396
397 // Backs up the crash and uploads it if allowed to.
398 HRESULT Crash::DoSendCrashReport(bool can_upload,
399 bool is_out_of_process,
400 const CString& crash_filename,
401 const ParameterMap& parameters,
402 CString* report_id) {
403 ASSERT1(!crash_filename.IsEmpty());
404 ASSERT1(report_id);
405 report_id->Empty();
406
407 if (!File::Exists(crash_filename)) {
408 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
409 }
410
411 CString product_name = GetProductName(parameters);
412 VERIFY1(SUCCEEDED(SaveLastCrash(crash_filename, product_name)));
413
414 HRESULT hr = S_OK;
415 if (can_upload) {
416 hr = UploadCrash(is_out_of_process, crash_filename, parameters, report_id);
417 } else {
418 CORE_LOG(L2, (_T("[crash uploads are not allowed]")));
419 }
420
421 return hr;
422 }
423
424 HRESULT Crash::UploadCrash(bool is_out_of_process,
425 const CString& crash_filename,
426 const ParameterMap& parameters,
427 CString* report_id) {
428 ASSERT1(report_id);
429 report_id->Empty();
430
431 // Calling this avoids crashes in WinINet. See: http://b/1258692
432 EnsureRasmanLoaded();
433
434 ASSERT1(!crash_dir_.IsEmpty());
435 ASSERT1(!checkpoint_file_.IsEmpty());
436
437 // Do best effort to send the crash. If it can't communicate with the backend,
438 // it retries a few times over a few hours time interval.
439 HRESULT hr = S_OK;
440 for (int i = 0; i != kCrashReportAttempts; ++i) {
441 std::wstring report_code;
442 CrashReportSender sender(checkpoint_file_.GetString());
443 sender.set_max_reports_per_day(max_reports_per_day_);
444 CORE_LOG(L2, (_T("[Uploading crash report]")
445 _T("[%s][%s]"), crash_report_url_, crash_filename));
446 ASSERT1(!crash_report_url_.IsEmpty());
447 ReportResult res = sender.SendCrashReport(crash_report_url_.GetString(),
448 parameters,
449 crash_filename.GetString(),
450 &report_code);
451 switch (res) {
452 case google_breakpad::RESULT_SUCCEEDED:
453 report_id->SetString(report_code.c_str());
454 hr = S_OK;
455 break;
456
457 case google_breakpad::RESULT_FAILED:
458 OPT_LOG(L2, (_T("[Crash report failed but it will retry sending]")));
459 ::Sleep(kCrashReportResendPeriodMs);
460 hr = E_FAIL;
461 break;
462
463 case google_breakpad::RESULT_REJECTED:
464 hr = GOOPDATE_E_CRASH_REJECTED;
465 break;
466
467 case google_breakpad::RESULT_THROTTLED:
468 hr = GOOPDATE_E_CRASH_THROTTLED;
469 break;
470
471 default:
472 hr = E_FAIL;
473 break;
474 };
475
476 // Continue the retry loop only when it could not contact the server.
477 if (res != google_breakpad::RESULT_FAILED) {
478 break;
479 }
480 }
481
482 CORE_LOG(L2, (_T("[crash report code = %s]"), *report_id));
483
484 // The event source for the out-of-process crashes is the product name.
485 // Therefore, in the event of an out-of-process crash, the log entry
486 // appears to be generated by the product that crashed.
487 CString product_name = is_out_of_process ? GetProductName(parameters) :
488 kAppName;
489 CString event_text;
490 uint16 event_type(0);
491 if (!report_id->IsEmpty()) {
492 event_type = EVENTLOG_INFORMATION_TYPE;
493 SafeCStringFormat(&event_text, _T("Crash uploaded. Id=%s."), *report_id);
494 } else {
495 ASSERT1(FAILED(hr));
496 event_type = EVENTLOG_WARNING_TYPE;
497 SafeCStringFormat(&event_text, _T("Crash not uploaded. Error=0x%x."), hr);
498 }
499 VERIFY1(SUCCEEDED(Crash::Log(event_type,
500 kCrashUploadEventId,
501 product_name,
502 event_text)));
503
504 UpdateCrashUploadMetrics(is_out_of_process, hr);
505
506 return hr;
507 }
508
509 HRESULT Crash::SaveLastCrash(const CString& crash_filename,
510 const CString& product_name) {
511 if (product_name.IsEmpty()) {
512 return E_INVALIDARG;
513 }
514 CString tmp;
515 SafeCStringFormat(&tmp, _T("%s-last.dmp"), product_name);
516 CString save_filename = ConcatenatePath(crash_dir_, tmp);
517 if (save_filename.IsEmpty()) {
518 return GOOPDATE_E_PATH_APPEND_FAILED;
519 }
520
521 CORE_LOG(L2, (_T("[Crash::SaveLastCrash]")
522 _T("[to %s][from %s]"), save_filename, crash_filename));
523
524 return ::CopyFile(crash_filename,
525 save_filename,
526 false) ? S_OK : HRESULTFromLastError();
527 }
528
529 HRESULT Crash::CleanStaleCrashes() {
530 CORE_LOG(L3, (_T("[Crash::CleanStaleCrashes]")));
531
532 // ??- sequence is a c++ trigraph corresponding to a ~. Escape it.
533 const TCHAR kWildCards[] = _T("???????\?-???\?-???\?-???\?-????????????.dmp");
534 std::vector<CString> crash_files;
535 HRESULT hr = File::GetWildcards(crash_dir_, kWildCards, &crash_files);
536 if (FAILED(hr)) {
537 return hr;
538 }
539
540 time64 now = GetCurrent100NSTime();
541 for (size_t i = 0; i != crash_files.size(); ++i) {
542 CORE_LOG(L3, (_T("[found crash file][%s]"), crash_files[i]));
543 FILETIME creation_time = {0};
544 if (SUCCEEDED(File::GetFileTime(crash_files[i],
545 &creation_time,
546 NULL,
547 NULL))) {
548 double time_diff =
549 static_cast<double>(now - FileTimeToTime64(creation_time));
550 if (abs(time_diff) >= kDaysTo100ns) {
551 VERIFY1(::DeleteFile(crash_files[i]));
552 }
553 }
554 }
555
556 return S_OK;
557 }
558
559 HRESULT Crash::Report(bool can_upload_in_process,
560 const CString& crash_filename,
561 const CString& custom_info_filename,
562 const CString& lang) {
563 const bool is_out_of_process = !custom_info_filename.IsEmpty();
564 HRESULT hr = S_OK;
565 if (is_out_of_process) {
566 hr = ReportProductCrash(true, crash_filename, custom_info_filename, lang);
567 ::DeleteFile(custom_info_filename);
568 } else {
569 hr = ReportGoogleUpdateCrash(can_upload_in_process,
570 crash_filename,
571 custom_info_filename,
572 lang);
573 }
574 ::DeleteFile(crash_filename);
575 CleanStaleCrashes();
576 return hr;
577 }
578
579 HRESULT Crash::ReportGoogleUpdateCrash(bool can_upload,
580 const CString& crash_filename,
581 const CString& custom_info_filename,
582 const CString& lang) {
583 OPT_LOG(L1, (_T("[Crash::ReportGoogleUpdateCrash]")));
584
585 UNREFERENCED_PARAMETER(custom_info_filename);
586
587 ++metric_crashes_total;
588
589 // Build the map of additional parameters to report along with the crash.
590 CString ver(GetVersionString() + version_postfix_);
591 CString uid = goopdate_utils::GetUserIdLazyInit(is_machine_);
592
593 ParameterMap parameters;
594 parameters[_T("prod")] = _T("Update2");
595 parameters[_T("ver")] = ver;
596 parameters[_T("userid")] = uid;
597 parameters[_T("lang")] = lang;
598
599 CString event_text;
600 SafeCStringFormat(&event_text,
601 _T("%s has encountered a fatal error.\r\n")
602 _T("ver=%s;lang=%s;id=%s;is_machine=%d;upload=%d;minidump=%s"),
603 kAppName,
604 ver, lang, uid, is_machine_, can_upload ? 1 : 0, crash_filename);
605 VERIFY1(SUCCEEDED(Crash::Log(EVENTLOG_ERROR_TYPE,
606 kCrashReportEventId,
607 kAppName,
608 event_text)));
609
610 CString report_id;
611 return DoSendCrashReport(can_upload,
612 false, // Omaha crash.
613 crash_filename,
614 parameters,
615 &report_id);
616 }
617
618 HRESULT Crash::ReportProductCrash(bool can_upload,
619 const CString& crash_filename,
620 const CString& custom_info_filename,
621 const CString& lang) {
622 OPT_LOG(L1, (_T("[Crash::ReportProductCrash]")));
623
624 UNREFERENCED_PARAMETER(lang);
625
626 // All product crashes must be uploaded.
627 ASSERT1(can_upload);
628
629 // Count the number of crashes the sender was requested to handle.
630 ++metric_oop_crashes_total;
631
632 std::map<CString, CString> parameters_temp;
633 HRESULT hr = goopdate_utils::ReadNameValuePairsFromFile(
634 custom_info_filename,
635 kCustomClientInfoGroup,
636 &parameters_temp);
637 if (FAILED(hr)) {
638 return hr;
639 }
640
641 ParameterMap parameters;
642 std::map<CString, CString>::const_iterator iter;
643 for (iter = parameters_temp.begin(); iter != parameters_temp.end(); ++iter) {
644 parameters[iter->first.GetString()] = iter->second.GetString();
645 }
646
647 const CString product_name = GetProductName(parameters);
648 const std::wstring ver = parameters[_T("ver")];
649
650 CString event_text;
651 SafeCStringFormat(&event_text,
652 _T("%s has encountered a fatal error.\r\n")
653 _T("ver=%s;is_machine=%d;minidump=%s"),
654 product_name, ver.c_str(), is_machine_, crash_filename);
655 VERIFY1(SUCCEEDED(Crash::Log(EVENTLOG_ERROR_TYPE,
656 kCrashReportEventId,
657 product_name,
658 event_text)));
659
660 CString report_id;
661 return hr = DoSendCrashReport(can_upload,
662 true, // Out of process crash.
663 crash_filename,
664 parameters,
665 &report_id);
666 }
667
668 void __stdcall Crash::EBHandler(EXCEPTION_POINTERS* ptrs) {
669 if (exception_handler_) {
670 exception_handler_->WriteMinidumpForException(ptrs);
671 }
672 }
673
674 HRESULT Crash::GetExceptionInfo(const CString& crash_filename,
675 MINIDUMP_EXCEPTION* ex_info) {
676 ASSERT1(ex_info);
677 ASSERT1(!crash_filename.IsEmpty());
678
679 // Dynamically link with the dbghelp to avoid runtime resource bloat.
680 scoped_library dbghelp(::LoadLibrary(_T("dbghelp.dll")));
681 if (!dbghelp) {
682 return HRESULTFromLastError();
683 }
684
685 typedef BOOL (WINAPI *MiniDumpReadDumpStreamFun)(void* base_of_dump,
686 ULONG stream_number,
687 MINIDUMP_DIRECTORY* dir,
688 void** stream_pointer,
689 ULONG* stream_size);
690
691 MiniDumpReadDumpStreamFun minidump_read_dump_stream =
692 reinterpret_cast<MiniDumpReadDumpStreamFun>(::GetProcAddress(get(dbghelp),
693 "MiniDumpReadDumpStream"));
694 ASSERT1(minidump_read_dump_stream);
695 if (!minidump_read_dump_stream) {
696 return HRESULTFromLastError();
697 }
698
699 // The minidump file must be mapped in memory before reading the streams.
700 scoped_hfile file(::CreateFile(crash_filename,
701 GENERIC_READ,
702 FILE_SHARE_READ,
703 NULL,
704 OPEN_EXISTING,
705 FILE_ATTRIBUTE_READONLY,
706 NULL));
707 ASSERT1(file);
708 if (!file) {
709 return HRESULTFromLastError();
710 }
711 scoped_file_mapping file_mapping(::CreateFileMapping(get(file),
712 NULL,
713 PAGE_READONLY,
714 0,
715 0,
716 NULL));
717 if (!file_mapping) {
718 return HRESULTFromLastError();
719 }
720 scoped_file_view base_of_dump(::MapViewOfFile(get(file_mapping),
721 FILE_MAP_READ,
722 0,
723 0,
724 0));
725 if (!base_of_dump) {
726 return HRESULTFromLastError();
727 }
728
729 // Read the exception stream and pick up the exception record.
730 MINIDUMP_DIRECTORY minidump_directory = {0};
731 void* stream_pointer = NULL;
732 ULONG stream_size = 0;
733 bool result = !!(*minidump_read_dump_stream)(get(base_of_dump),
734 ExceptionStream,
735 &minidump_directory,
736 &stream_pointer,
737 &stream_size);
738 if (!result) {
739 return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
740 }
741 MINIDUMP_EXCEPTION_STREAM* exception_stream =
742 static_cast<MINIDUMP_EXCEPTION_STREAM*>(stream_pointer);
743 ASSERT1(stream_pointer);
744 ASSERT1(stream_size);
745
746 *ex_info = exception_stream->ExceptionRecord;
747 return S_OK;
748 }
749
750 bool Crash::IsInteractive() {
751 bool result = false;
752 ::EnumWindows(&Crash::EnumWindowsCallback, reinterpret_cast<LPARAM>(&result));
753 return result;
754 }
755
756 // Finds if the given window is in the current process.
757 BOOL CALLBACK Crash::EnumWindowsCallback(HWND hwnd, LPARAM param) {
758 DWORD pid = 0;
759 ::GetWindowThreadProcessId(hwnd, &pid);
760 if (::IsWindowVisible(hwnd) && pid == ::GetCurrentProcessId() && param) {
761 *reinterpret_cast<bool*>(param) = true;
762 return false;
763 }
764 return true;
765 }
766
767 HRESULT Crash::InitializeCrashDir() {
768 crash_dir_.Empty();
769 ConfigManager* cm = ConfigManager::Instance();
770 CString dir = is_machine_ ? cm->GetMachineCrashReportsDir() :
771 cm->GetUserCrashReportsDir();
772
773 if (is_machine_ && !dir.IsEmpty()) {
774 HRESULT hr = InitializeDirSecurity(&dir);
775 if (FAILED(hr)) {
776 CORE_LOG(LW, (_T("[failed to initialize crash dir security][0x%x]"), hr));
777 ::RemoveDirectory(dir);
778 }
779 }
780
781 // Use the temporary directory of the process if the crash directory can't be
782 // initialized for any reason. Users can't read files in other users'
783 // temporary directories so the temp dir is good option to still have
784 // crash handling.
785 if (dir.IsEmpty()) {
786 dir = app_util::GetTempDir();
787 }
788
789 if (dir.IsEmpty()) {
790 return GOOPDATE_E_CRASH_NO_DIR;
791 }
792
793 crash_dir_ = dir;
794 return S_OK;
795 }
796
797 HRESULT Crash::InitializeDirSecurity(CString* dir) {
798 ASSERT1(dir);
799
800 // Users can only read permissions on the crash dir.
801 CDacl dacl;
802 const uint8 kAceFlags = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
803 if (!dacl.AddAllowedAce(Sids::System(), GENERIC_ALL, kAceFlags) ||
804 !dacl.AddAllowedAce(Sids::Admins(), GENERIC_ALL, kAceFlags) ||
805 !dacl.AddAllowedAce(Sids::Users(), READ_CONTROL, kAceFlags)) {
806 return GOOPDATE_E_CRASH_SECURITY_FAILED;
807 }
808
809 SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION |
810 PROTECTED_DACL_SECURITY_INFORMATION;
811 DWORD error = ::SetNamedSecurityInfo(dir->GetBuffer(),
812 SE_FILE_OBJECT,
813 si,
814 NULL,
815 NULL,
816 const_cast<ACL*>(dacl.GetPACL()),
817 NULL);
818 if (error != ERROR_SUCCESS) {
819 return HRESULT_FROM_WIN32(error);
820 }
821 return S_OK;
822 }
823
824 // Checks for the presence of an environment variable. We are not interested
825 // in the value of the variable but only in its presence.
826 HRESULT Crash::IsCrashReportProcess(bool* is_crash_report_process) {
827 ASSERT1(is_crash_report_process);
828 if (::GetEnvironmentVariable(kNoCrashHandlerEnvVariableName, NULL, 0)) {
829 *is_crash_report_process = true;
830 return S_OK;
831 } else {
832 DWORD error(::GetLastError());
833 *is_crash_report_process = false;
834 return error == ERROR_ENVVAR_NOT_FOUND ? S_OK : HRESULT_FROM_WIN32(error);
835 }
836 }
837
838 HRESULT Crash::Log(uint16 type,
839 uint32 id,
840 const TCHAR* source,
841 const TCHAR* description) {
842 ASSERT1(source);
843 ASSERT1(description);
844 return EventLogger::ReportEvent(source,
845 type,
846 0, // Category.
847 id,
848 1, // Number of strings.
849 &description,
850 0, // Raw data size.
851 NULL); // Raw data.
852 }
853
854 CString Crash::GetProductName(const ParameterMap& parameters) {
855 // The crash is logged using the value of 'prod' if available or
856 // a default constant string otherwise.
857 CString product_name;
858 ParameterMap::const_iterator it = parameters.find(_T("prod"));
859 const bool is_found = it != parameters.end() && !it->second.empty();
860 return is_found ? it->second.c_str() : kDefaultProductName;
861 }
862
863 void Crash::UpdateCrashUploadMetrics(bool is_out_of_process, HRESULT hr) {
864 switch (hr) {
865 case S_OK:
866 if (is_out_of_process) {
867 ++metric_oop_crashes_uploaded;
868 } else {
869 ++metric_crashes_uploaded;
870 }
871 break;
872
873 case E_FAIL:
874 if (is_out_of_process) {
875 ++metric_oop_crashes_failed;
876 } else {
877 ++metric_crashes_failed;
878 }
879 break;
880
881 case GOOPDATE_E_CRASH_THROTTLED:
882 if (is_out_of_process) {
883 ++metric_oop_crashes_throttled;
884 } else {
885 ++metric_crashes_throttled;
886 }
887 break;
888
889 case GOOPDATE_E_CRASH_REJECTED:
890 if (is_out_of_process) {
891 ++metric_oop_crashes_rejected;
892 } else {
893 ++metric_crashes_rejected;
894 }
895 break;
896
897 default:
898 ASSERT1(false);
899 break;
900 }
901 }
902
903 int Crash::CrashNow() {
904 #ifdef DEBUG
905 CORE_LOG(LEVEL_ERROR, (_T("[Crash::CrashNow]")));
906 int foo = 10;
907 int bar = foo - 10;
908 int baz = foo / bar;
909 return baz;
910 #else
911 return 0;
912 #endif
913 }
914
915 HRESULT Crash::CreateLowIntegrityDesc(CSecurityDesc* sd) {
916 ASSERT1(sd);
917 ASSERT1(!sd->GetPSECURITY_DESCRIPTOR());
918
919 if (!vista_util::IsVistaOrLater()) {
920 return S_FALSE;
921 }
922
923 return sd->FromString(LOW_INTEGRITY_SDDL_SACL) ? S_OK :
924 HRESULTFromLastError();
925 }
926
927 bool Crash::AddPipeSecurityDaclToDesc(bool is_machine, CSecurityDesc* sd) {
928 ASSERT1(sd);
929
930 CAccessToken current_token;
931 if (!current_token.GetEffectiveToken(TOKEN_QUERY)) {
932 OPT_LOG(LE, (_T("[Failed to get current thread token]")));
933 return false;
934 }
935
936 CDacl dacl;
937 if (!current_token.GetDefaultDacl(&dacl)) {
938 OPT_LOG(LE, (_T("[Failed to get default DACL]")));
939 return false;
940 }
941
942 if (!is_machine) {
943 sd->SetDacl(dacl);
944 return true;
945 }
946
947 if (!dacl.AddAllowedAce(ATL::Sids::Users(), kPipeAccessMask)) {
948 OPT_LOG(LE, (_T("[Failed to setup pipe security]")));
949 return false;
950 }
951
952 if (!dacl.AddDeniedAce(ATL::Sids::Network(), FILE_ALL_ACCESS)) {
953 OPT_LOG(LE, (_T("[Failed to setup pipe security]")));
954 return false;
955 }
956
957 sd->SetDacl(dacl);
958 return true;
959 }
960
961 bool Crash::BuildPipeSecurityAttributes(bool is_machine,
962 CSecurityAttributes* sa) {
963 ASSERT1(sa);
964
965 CSecurityDesc sd;
966 HRESULT hr = CreateLowIntegrityDesc(&sd);
967 if (FAILED(hr)) {
968 OPT_LOG(LE, (_T("[Failed to CreateLowIntegrityDesc][0x%x]"), hr));
969 return false;
970 }
971
972 if (!AddPipeSecurityDaclToDesc(is_machine, &sd)) {
973 return false;
974 }
975
976 sa->Set(sd);
977
978 #ifdef _DEBUG
979 // Print SDDL for debugging.
980 CString sddl;
981 sd.ToString(&sddl, OWNER_SECURITY_INFORMATION |
982 GROUP_SECURITY_INFORMATION |
983 DACL_SECURITY_INFORMATION |
984 SACL_SECURITY_INFORMATION |
985 LABEL_SECURITY_INFORMATION);
986 CORE_LOG(L1, (_T("[Pipe security SDDL][%s]"), sddl));
987 #endif
988
989 return true;
990 }
991
992 bool Crash::BuildCrashDirSecurityAttributes(CSecurityAttributes* sa) {
993 ASSERT1(sa);
994
995 CDacl dacl;
996 CAccessToken current_token;
997
998 if (!current_token.GetEffectiveToken(TOKEN_QUERY)) {
999 OPT_LOG(LE, (_T("[Failed to get current thread token]")));
1000 return false;
1001 }
1002
1003 if (!current_token.GetDefaultDacl(&dacl)) {
1004 OPT_LOG(LE, (_T("[Failed to get default DACL]")));
1005 return false;
1006 }
1007
1008 CSecurityDesc sd;
1009 sd.SetDacl(dacl);
1010 // Prevent the security settings on the parent folder of CrashReports folder
1011 // from being inherited by children of CrashReports folder.
1012 sd.SetControl(SE_DACL_PROTECTED, SE_DACL_PROTECTED);
1013 sa->Set(sd);
1014
1015 #ifdef _DEBUG
1016 // Print SDDL for debugging.
1017 CString sddl;
1018 sd.ToString(&sddl);
1019 CORE_LOG(L1, (_T("[Folder security SDDL][%s]"), sddl));
1020 #endif
1021
1022 return true;
1023 }
1024
1025 HRESULT Crash::StartServer() {
1026 CORE_LOG(L1, (_T("[Crash::StartServer]")));
1027 ++metric_crash_start_server_total;
1028
1029 std::wstring dump_path(crash_dir_);
1030
1031 // Append the current user's sid to the pipe name so that machine and
1032 // user instances of the crash server open different pipes.
1033 CString user_sid;
1034 HRESULT hr = user_info::GetProcessUser(NULL, NULL, &user_sid);
1035 if (FAILED(hr)) {
1036 OPT_LOG(LE, (_T("[user_info::GetProcessUser failed][0x%08x]"), hr));
1037 return hr;
1038 }
1039 CString pipe_name(kCrashPipeNamePrefix);
1040 SafeCStringAppendFormat(&pipe_name, _T("\\%s"), user_sid);
1041
1042 CSecurityAttributes pipe_sec_attrs;
1043
1044 // * If running as machine, use custom security attributes on the pipe to
1045 // allow all users on the local machine to connect to it. Add the low
1046 // integrity SACL to account for browser plugins.
1047 // * If running as user, the default security descriptor for the
1048 // pipe grants full control to the system account, administrators,
1049 // and the creator owner. Add the low integrity SACL to account for browser
1050 // plugins.
1051 if (!BuildPipeSecurityAttributes(is_machine_, &pipe_sec_attrs)) {
1052 return GOOPDATE_E_CRASH_SECURITY_FAILED;
1053 }
1054
1055 scoped_ptr<CrashGenerationServer> crash_server(
1056 new CrashGenerationServer(std::wstring(pipe_name),
1057 &pipe_sec_attrs,
1058 ClientConnectedCallback, NULL,
1059 ClientCrashedCallback, NULL,
1060 ClientExitedCallback, NULL,
1061 true, &dump_path));
1062
1063 if (!crash_server->Start()) {
1064 CORE_LOG(LE, (_T("[CrashServer::Start failed]")));
1065 return GOOPDATE_E_CRASH_START_SERVER_FAILED;
1066 }
1067
1068 crash_server_ = crash_server.release();
1069
1070 ++metric_crash_start_server_succeeded;
1071 return S_OK;
1072 }
1073
1074 void Crash::StopServer() {
1075 CORE_LOG(L1, (_T("[Crash::StopServer]")));
1076 delete crash_server_;
1077 crash_server_ = NULL;
1078 }
1079
1080
1081 void _cdecl Crash::ClientConnectedCallback(void* context,
1082 const ClientInfo* client_info) {
1083 ASSERT1(!context);
1084 ASSERT1(client_info);
1085 UNREFERENCED_PARAMETER(context);
1086 OPT_LOG(L1, (_T("[Client connected][%d]"), client_info->pid()));
1087 }
1088
1089 void _cdecl Crash::ClientCrashedCallback(void* context,
1090 const ClientInfo* client_info,
1091 const std::wstring* dump_path) {
1092 ASSERT1(!context);
1093 ASSERT1(client_info);
1094 UNREFERENCED_PARAMETER(context);
1095 OPT_LOG(L1, (_T("[Client crashed][%d]"), client_info->pid()));
1096
1097 CString crash_filename(dump_path ? dump_path->c_str() : NULL);
1098 HRESULT hr = Crash::CrashHandler(is_machine_, *client_info, crash_filename);
1099 if (FAILED(hr)) {
1100 OPT_LOG(LE, (_T("[CrashHandler failed][0x%08x]"), hr));
1101 }
1102 }
1103
1104 void _cdecl Crash::ClientExitedCallback(void* context,
1105 const ClientInfo* client_info) {
1106 ASSERT1(!context);
1107 ASSERT1(client_info);
1108 UNREFERENCED_PARAMETER(context);
1109 OPT_LOG(L1, (_T("[Client exited][%d]"), client_info->pid()));
1110 }
1111
1112 } // namespace omaha
1113
OLDNEW
« no previous file with comments | « goopdate/crash.h ('k') | goopdate/crash_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698