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

Side by Side Diff: base/debug.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 | « base/debug.h ('k') | base/debug_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 2003-2009 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 // Debug functions
17
18 #include "omaha/base/debug.h"
19
20 #include <dbghelp.h>
21 #include <wtsapi32.h>
22 #include <atlstr.h>
23 #ifdef _DEBUG
24 #include <atlcom.h>
25 #define STRSAFE_NO_DEPRECATE
26 #include <strsafe.h>
27 #endif
28 #include <stdlib.h>
29 #include <signal.h>
30 #include "base/basictypes.h"
31 #include "base/scoped_ptr.h"
32 #include "omaha/base/app_util.h"
33 #include "omaha/base/clipboard.h"
34 #include "omaha/base/commontypes.h"
35 #include "omaha/base/constants.h"
36 #include "omaha/base/const_addresses.h"
37 #include "omaha/base/const_config.h"
38 #include "omaha/base/const_debug.h"
39 #include "omaha/base/const_timeouts.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/reg_key.h"
45 #include "omaha/base/safe_format.h"
46 #include "omaha/base/scope_guard.h"
47 #include "omaha/base/scoped_ptr_address.h"
48 #include "omaha/base/string.h"
49 #include "omaha/base/system.h"
50 #include "omaha/base/synchronized.h"
51 #include "omaha/base/time.h"
52 #include "omaha/base/utils.h"
53 #include "omaha/base/vistautil.h"
54 #include "omaha/base/vista_utils.h"
55
56 namespace omaha {
57
58 #ifdef _DEBUG
59 #define kSprintfBuffers (100) // number of buffers for SPRINTF
60 #else
61 #define kSprintfBuffers (3) // number of buffers for SPRINTF
62 #endif
63
64 // pad SPRINTF buffer to check for overruns
65 #if SHIPPING
66 #define kSprintfBufferOverrunPadding 0
67 #else // !SHIPPING
68 #ifdef DEBUG
69 #define kSprintfBufferOverrunPadding 20000
70 #else
71 #define kSprintfBufferOverrunPadding 1024
72 #endif // DEBUG
73 #endif // SHIPPING
74
75 #ifdef _DEBUG
76 const TCHAR* const kErrorRequestToSendFormat =
77 _T("*** Please hit Ignore to continue and send error information to the ")
78 _T("%s team ***\n*** These details have been pasted to the clipboard ***");
79
80 // Max length of report summary string.
81 const int kMaxReportSummaryLen = 1024 * 100;
82 #endif // DEBUG
83
84 #define kReportIdsLock kLockPrefix \
85 _T("Report_Ids_Lock_57146B01-6A07-4b8d-A1D8-0C3AFC3B2F9B")
86
87 SELECTANY bool g_always_assert = false;
88 SELECTANY TCHAR *g_additional_status_ping_info = NULL;
89
90 #define kSprintfMaxLen (1024 + 2) // max length that wvsprintf writes is 1024
91 static bool g_initialized_sprintf = false;
92 static volatile LONG g_sprintf_interlock = 0;
93 static int g_current_sprintf_buffer = 0;
94 static TCHAR *g_sprintf_buffer = NULL;
95 static TCHAR *g_sprintf_buffers[kSprintfBuffers];
96 SELECTANY volatile LONG g_debugassertrecursioncheck = 0;
97 static int g_total_reports = 0;
98
99 SELECTANY ReportIds g_report_ids;
100
101 // Builds a full path name out of the given filename. If the filename is
102 // a relative path, it is appended to the debug directory. Otherwise, if the
103 // filename is a full path, it returns it as the full debug filename.
104 static CString MakeFullDebugFilename(const TCHAR *filename) {
105 CString full_name;
106 if (lstrlen(filename) <= 2 || filename[1] != _T(':')) {
107 full_name = GetDebugDirectory();
108 full_name += L"\\";
109 }
110 full_name += filename;
111 return full_name;
112 }
113
114
115 // Displays the assert box. Due to session isolation, MB_SERVICE_NOTIFICATION
116 // flag does not work for Vista services. In this case, use WTS to display
117 // a message box in the active console session.
118 void ShowAssertDialog(const TCHAR *message, const TCHAR *title) {
119 int ret = 0;
120 OSVERSIONINFOEX osviex = {sizeof(OSVERSIONINFOEX), 0};
121 const bool is_vista_or_greater =
122 ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osviex)) &&
123 osviex.dwMajorVersion >= 6;
124 bool is_system_process = false;
125 if (is_vista_or_greater &&
126 SUCCEEDED(IsSystemProcess(&is_system_process)) &&
127 is_system_process) {
128 DWORD session_id = System::WTSGetActiveConsoleSessionId();
129 if (session_id == kInvalidSessionId) {
130 session_id = WTS_CURRENT_SESSION;
131 }
132 DWORD response = 0;
133 ::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE,
134 session_id,
135 const_cast<TCHAR*>(title),
136 _tcslen(title) * sizeof(TCHAR),
137 const_cast<TCHAR*>(message),
138 _tcslen(message) * sizeof(TCHAR),
139 MB_ABORTRETRYIGNORE | MB_ICONERROR,
140 0,
141 &response,
142 true);
143 ret = response;
144 } else {
145 ret = ::MessageBoxW(NULL,
146 message,
147 title,
148 MB_ABORTRETRYIGNORE |
149 MB_ICONERROR |
150 MB_SERVICE_NOTIFICATION);
151 }
152
153 switch (ret) {
154 case IDABORT:
155 // Terminate the process if the user chose 'Abort'. Calling ExitProcess
156 // here results in calling the destructors for static objects which can
157 // result in deadlocks.
158 raise(SIGABRT);
159 break;
160
161 case IDRETRY:
162 // Break if the user chose "Retry".
163 __debugbreak();
164 break;
165 default:
166 // By default we ignore the message.
167 break;
168 }
169 }
170
171 DebugObserver* g_debug_observer = NULL;
172
173 // replaces the debug observer, returns the previous value.
174 DebugObserver* SetDebugObserver(DebugObserver* observer) {
175 DebugObserver* old_value = g_debug_observer;
176 g_debug_observer = observer;
177 return old_value;
178 }
179
180 DebugObserver* PeekDebugObserver() {
181 return g_debug_observer;
182 }
183
184 int SehSendMinidump(unsigned int code,
185 struct _EXCEPTION_POINTERS *ep,
186 time64 time_between_minidumps) {
187 if (code == EXCEPTION_BREAKPOINT)
188 return EXCEPTION_CONTINUE_SEARCH;
189
190 if (::IsDebuggerPresent())
191 return EXCEPTION_CONTINUE_SEARCH;
192
193 OutputDebugString(L"**SehSendMinidump**\r\n");
194
195 if (g_debug_observer) {
196 return g_debug_observer->SehSendMinidump(code, ep, time_between_minidumps);
197 }
198
199 return EXCEPTION_EXECUTE_HANDLER;
200 }
201
202 #if defined(_DEBUG) || defined(ASSERT_IN_RELEASE)
203 CallInterceptor<DebugAssertFunctionType> debug_assert_interceptor;
204
205 // Replaces the debug assert function; returns the old value.
206 DebugAssertFunctionType* ReplaceDebugAssertFunction(
207 DebugAssertFunctionType* replacement) {
208 return debug_assert_interceptor.ReplaceFunction(replacement);
209 }
210
211 void OnAssert(const char *expr, const TCHAR *msg,
212 const char *filename, int32 linenumber) {
213 if (g_debug_observer) {
214 g_debug_observer->OnAssert(expr, msg, filename, linenumber);
215 }
216 }
217 #endif
218
219 #if defined(_DEBUG)
220 CString OnDebugReport(uint32 id,
221 bool is_report,
222 ReportType type,
223 const char *expr,
224 const TCHAR *message,
225 const char *filename,
226 int32 linenumber,
227 DebugReportKind debug_report_kind) {
228 CString trace;
229 if (g_debug_observer) {
230 trace = g_debug_observer->OnDebugReport(id, is_report,
231 type, expr, message, filename,
232 linenumber, debug_report_kind);
233 }
234 return trace;
235 }
236
237 void SendExceptionReport(const TCHAR *log_file, const TCHAR *filename, int line,
238 const TCHAR *type, uint32 id, bool offline) {
239 if (g_debug_observer) {
240 g_debug_observer->SendExceptionReport(log_file, filename, line,
241 type, id, offline);
242 }
243 }
244 #endif
245
246
247 #ifdef _DEBUG // won't compile since _CrtDbgReport isn't defined.
248
249 #include <crtdbg.h> // NOLINT
250 static CString g_report_summary;
251
252 // dump summary of reports on exit
253 SELECTANY ReportSummaryGenerator g_report_summary_generator;
254
255 ReportSummaryGenerator::~ReportSummaryGenerator() {
256 DumpReportSummary();
257 }
258
259 void ReportSummaryGenerator::DumpReportSummary() {
260 if (g_total_reports) {
261 ::OutputDebugString(L"REPORT SUMMARY:\r\n");
262 ::OutputDebugString(SPRINTF(L"%d total reports\r\n", g_total_reports));
263 ::OutputDebugString(g_report_summary);
264 } else {
265 ::OutputDebugString(L"NO REPORTS!!\r\n");
266 }
267 }
268
269 TCHAR *ReportSummaryGenerator::GetReportSummary() {
270 TCHAR *s = new TCHAR[kMaxReportSummaryLen];
271 if (s) {
272 s[0] = 0;
273 if (g_total_reports) {
274 SafeStrCat(s, L"REPORT SUMMARY:\r\n\r\n", kMaxReportSummaryLen);
275 SafeStrCat(s,
276 SPRINTF(L"%d total reports\r\n\r\n", g_total_reports),
277 kMaxReportSummaryLen);
278 SafeStrCat(s,
279 g_report_summary.
280 Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(),
281 kMaxReportSummaryLen);
282 CString report_string = g_report_ids.DebugReportString();
283 ReplaceCString(report_string, L"&", L"\r\n");
284 SafeStrCat(s,
285 report_string.
286 Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(),
287 kMaxReportSummaryLen);
288 } else {
289 SafeStrCat(s, L"NO REPORTS!!\r\n", kMaxReportSummaryLen);
290 }
291 }
292
293 return s;
294 }
295
296 static CAtlMap<CString, uint32> g_reports_done;
297
298 #endif // _DEBUG
299
300 #ifdef _DEBUG
301
302 void TraceError(DWORD error) {
303 HLOCAL mem = NULL;
304 ::FormatMessage(
305 FORMAT_MESSAGE_ALLOCATE_BUFFER |
306 FORMAT_MESSAGE_FROM_SYSTEM |
307 FORMAT_MESSAGE_IGNORE_INSERTS,
308 static_cast<LPVOID>(_AtlBaseModule.GetResourceInstance()),
309 error,
310 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
311 reinterpret_cast<TCHAR*>(&mem),
312 0,
313 NULL);
314
315 TCHAR* str = reinterpret_cast<TCHAR*>(::LocalLock(mem));
316 ::OutputDebugString(str);
317 REPORT(false, R_ERROR, (str), 3968294226);
318 ::LocalFree(mem);
319 }
320
321 // ban ASSERT/VERIFY/REPORT to prevent recursion
322 #undef ASSERT
323 #undef VERIFY
324 #undef REPORT
325
326 // TODO(omaha): fix static initialization order below.
327 // The initialization order of static variables is not deterministic per C++
328 // standard and it depends completely on the compiler implementation.
329 // For VC++ compiler we are using, it seems to work as expected.
330 // One real fix is to put all definitons of static variables inside a class and
331 // define a boolean variable in this class to indicate that all necessary
332 // static initializations have been done.
333 //
334 // The follow definition is used to detect whether we get the exception
335 // during initializing static variables. If this is the case, DebugReport()
336 // will not function and will throw an exception because some of its refering
337 // static variables are not initialized yet (i.e. g_reports_done).
338 const int kTestInitStaticVariablesDoneValue = 1234;
339 struct TestInitStaticVariablesDone {
340 int value;
341 TestInitStaticVariablesDone() : value(kTestInitStaticVariablesDoneValue) {}
342 };
343 static TestInitStaticVariablesDone test_var;
344
345 bool DebugReport(unsigned int id,
346 ReportType type,
347 const char *expr,
348 const TCHAR *message,
349 const char *filename,
350 int linenumber,
351 DebugReportKind debug_report_kind) {
352 int recursion_count = ::InterlockedIncrement(&g_debugassertrecursioncheck);
353 ON_SCOPE_EXIT(::InterlockedDecrement, &g_debugassertrecursioncheck);
354 if (recursion_count > 1) {
355 ::OutputDebugString(_T("recursive debugreport skipped\n"));
356 return 1;
357 }
358
359 if (debug_assert_interceptor.interceptor()) {
360 // call replacement function (typically used for unit tests)
361 // Note that I'm doing this inside the in_assert block for paranoia;
362 // it's not really necessary and perhaps the wrong choice.
363 debug_assert_interceptor.interceptor()(expr, CT2A(message), filename,
364 linenumber);
365 return true;
366 }
367
368
369 // Check whether we have already finished initializing all static variables
370 // needed for executing DebugReport(). If not, bail out.
371 if (test_var.value != kTestInitStaticVariablesDoneValue) {
372 CString debug_msg;
373 SafeCStringFormat(&debug_msg, _T("%hs:%d - %s - %S"),
374 filename, linenumber, message, expr);
375 debug_msg.Append(_T("\n\nException occurs while initializing ")
376 _T("static variables needed for DebugReport"));
377 ShowAssertDialog(debug_msg, _T("DebugReport"));
378 return true;
379 }
380
381 bool is_assert = debug_report_kind == DEBUGREPORT_ASSERT;
382 bool is_report = debug_report_kind == DEBUGREPORT_REPORT;
383 bool is_abort = debug_report_kind == DEBUGREPORT_ABORT;
384
385 if (is_report)
386 g_total_reports++;
387
388 g_report_ids.ReleaseReport(id);
389
390 if (type == R_FATAL) {
391 if (is_report) {
392 // Treat as ASSERT
393 is_report = false;
394 is_assert = true;
395 }
396 }
397
398 bool always_assert = g_always_assert;
399
400 if (always_assert) {
401 is_report = false;
402 is_assert = true;
403 }
404
405 if (!message) {
406 message = _T("");
407 }
408
409 // log to debugger
410 TCHAR *debug_string;
411 // ::OutputDebugString(DEBUG_LOG_SEPARATOR);
412 ::OutputDebugString(is_report ? _T("REPORT: ") :
413 (is_assert ? _T("ASSERT: ") : _T("ABORT: ")));
414
415 CFixedStringT<CString, 1024> proc_name = app_util::GetAppName();
416
417 // last %s now %s skip %d
418 const TCHAR* format = message && *message ?
419 _T("[%hs:%d][%hs][%s]") : _T("[%hs:%d][%hs]");
420 debug_string = SPRINTF(format, filename, linenumber, expr, message);
421
422 // String_Int64ToString(g_last_report_time, 10),
423 // String_Int64ToString(time, 10), skip_report));
424
425 // ::OutputDebugString(DEBUG_LOG_SEPARATOR);
426 // ::OutputDebugString(_T("\n"));
427
428 #ifdef LOGGING
429 // Log the reports via the logging system to all loggers.
430 CString what = is_report ? _T("REPORT") :
431 is_assert ? _T("ASSERT") : _T("ABORT");
432 LC_LOG(LC_LOGGING, LEVEL_ERROR, (_T("[%s]%s"), what, debug_string));
433 #else
434 ::OutputDebugString(debug_string);
435 ::OutputDebugString(_T("\n"));
436 #endif
437
438 // skip sending strack trace for duplicate reports
439 CString report_id;
440 SafeCStringFormat(&report_id, _T("%hs:%d"), filename, linenumber);
441
442 uint32 prev_reports = 0;
443 if (g_reports_done.Lookup(report_id, prev_reports) && is_report) {
444 prev_reports++;
445 g_reports_done.SetAt(report_id, prev_reports);
446 ::OutputDebugString(SPRINTF(_T("skipping duplicate report %s %d\n"),
447 report_id.GetString(),
448 prev_reports));
449 return 1;
450 }
451
452 prev_reports++;
453 g_reports_done.SetAt(report_id, prev_reports);
454
455 g_report_summary.Append(debug_string);
456 g_report_summary.Append(L" (");
457 g_report_summary.Append(itostr(id));
458 g_report_summary.Append(L")");
459 g_report_summary.Append(L"\r\n");
460
461 // ::OutputDebugString(_T("log to file\n"));
462
463 // log to file
464 CString path_name(MakeFullDebugFilename(kCiDebugLogFile));
465 HANDLE h = CreateFile(path_name,
466 GENERIC_WRITE | GENERIC_READ,
467 FILE_SHARE_READ | FILE_SHARE_WRITE,
468 NULL,
469 OPEN_ALWAYS,
470 FILE_ATTRIBUTE_NORMAL,
471 NULL);
472
473 HANDLE assert_file = INVALID_HANDLE_VALUE;
474 if (is_assert) {
475 path_name = MakeFullDebugFilename(kCiAssertOccurredFile);
476 assert_file = CreateFile(path_name,
477 GENERIC_WRITE,
478 0,
479 0,
480 OPEN_ALWAYS,
481 FILE_FLAG_WRITE_THROUGH,
482 NULL);
483 }
484
485 HANDLE abort_file = INVALID_HANDLE_VALUE;
486 if (is_abort) {
487 path_name = MakeFullDebugFilename(kCiAbortOccurredFile);
488 abort_file = CreateFile(path_name,
489 GENERIC_WRITE,
490 0,
491 0,
492 OPEN_ALWAYS,
493 FILE_FLAG_WRITE_THROUGH,
494 NULL);
495 }
496
497 if (h != INVALID_HANDLE_VALUE ||
498 assert_file != INVALID_HANDLE_VALUE ||
499 abort_file != INVALID_HANDLE_VALUE) {
500 // more convenient for now to have this in UTF8
501 char *utf8_buffer = new char[(lstrlen(debug_string)*2) + 1];
502 if (utf8_buffer) {
503 int conv_bytes = WideCharToMultiByte(CP_UTF8,
504 0,
505 debug_string,
506 lstrlen(debug_string),
507 utf8_buffer,
508 (lstrlen(debug_string) * 2) + 1,
509 NULL,
510 NULL);
511
512 if (conv_bytes) {
513 DWORD bytes_written;
514 BOOL result;
515
516 if (h != INVALID_HANDLE_VALUE) {
517 SetFilePointer(h, 0, NULL, FILE_END);
518 result = ::WriteFile(h,
519 (LPCVOID)utf8_buffer,
520 conv_bytes,
521 &bytes_written,
522 NULL);
523 result = ::WriteFile(h,
524 (LPCVOID)DEBUG_LOG_SEPARATOR_CHAR,
525 strlen(DEBUG_LOG_SEPARATOR_CHAR),
526 &bytes_written,
527 NULL);
528 }
529 if (assert_file != INVALID_HANDLE_VALUE) {
530 result = ::WriteFile(assert_file,
531 (LPCVOID)utf8_buffer,
532 conv_bytes,
533 &bytes_written,
534 NULL);
535 }
536 if (abort_file != INVALID_HANDLE_VALUE) {
537 result = ::WriteFile(abort_file,
538 (LPCVOID)utf8_buffer,
539 conv_bytes,
540 &bytes_written,
541 NULL);
542 }
543 }
544
545 delete [] utf8_buffer;
546 }
547 }
548
549 if (h != INVALID_HANDLE_VALUE) {
550 ::CloseHandle(h);
551 }
552 if (assert_file != INVALID_HANDLE_VALUE) {
553 ::CloseHandle(assert_file);
554 }
555 if (abort_file != INVALID_HANDLE_VALUE) {
556 ::CloseHandle(abort_file);
557 }
558
559 CString stack_trace = OnDebugReport(id, is_report, type, expr, message,
560 filename, linenumber, debug_report_kind);
561
562 if (is_report) {
563 return 1;
564 }
565
566 ::OutputDebugString(L"show assert dialog\r\n");
567 ::OutputDebugString(stack_trace.GetString());
568
569 CString process_path;
570 GetModuleFileName(NULL, &process_path);
571
572 static TCHAR clipboard_string[4096] = {0};
573 lstrcpyn(clipboard_string,
574 SPRINTF(L"%ls (pid=%i)\r\n%hs:%d\r\n\r\n%hs\r\n%s\r\n\r\n",
575 process_path,
576 ::GetCurrentProcessId(),
577 filename,
578 linenumber,
579 expr,
580 message),
581 arraysize(clipboard_string));
582 stack_trace = stack_trace.Left(
583 arraysize(clipboard_string) - lstrlen(clipboard_string) - 1);
584 SafeStrCat(clipboard_string, stack_trace, arraysize(clipboard_string));
585 SetClipboard(clipboard_string);
586
587 stack_trace = stack_trace.Left(kMaxStackTraceDialogLen);
588
589 CString assert_text;
590 SafeCStringFormat(&assert_text,
591 _T("Assertion (%ls) failed!\r\n\r\nProcess: %d ")
592 _T("(0x%08X)\r\nThread %d (0x%08X)\r\nProgram: %ls\r\n")
593 _T("Version: %s\r\nFile: %hs\r\nLine: %d\r\n\r\n"),
594 debug_report_kind == DEBUGREPORT_ASSERT ?
595 L"Assert" : (debug_report_kind == DEBUGREPORT_ABORT ?
596 L"Abort" : L"Report"),
597 ::GetCurrentProcessId(), ::GetCurrentProcessId(), ::GetCurrentThreadId(),
598 ::GetCurrentThreadId(), process_path, omaha::GetVersionString(), filename,
599 linenumber);
600
601 CString error_request_to_send;
602 SafeCStringFormat(&error_request_to_send, kErrorRequestToSendFormat, kAppName) ;
603
604 if (lstrlen(message) > 0) {
605 SafeCStringAppendFormat(&assert_text,
606 _T("Expression: %hs\r\nMessage: %s\r\n\r\n%s\r\n\r\n%s"),
607 expr,
608 message,
609 stack_trace,
610 error_request_to_send);
611 } else {
612 SafeCStringAppendFormat(&assert_text,
613 _T("Expression: %hs\r\n\r\n%s\r\n\r\n%s"),
614 expr, stack_trace, error_request_to_send);
615 }
616
617 ShowAssertDialog(assert_text, CString(filename));
618 return 1;
619 }
620
621 #endif // #ifdef _DEBUG
622
623
624 ReportIds::ReportIds() {
625 data_.report_counts_num = 0;
626 NamedObjectAttributes lock_attr;
627 GetNamedObjectAttributes(kReportIdsLock,
628 vista_util::IsUserAdmin(),
629 &lock_attr);
630 InitializeWithSecAttr(lock_attr.name, &lock_attr.sa);
631 }
632
633 ReportIds::~ReportIds() {
634 // don't attempt to write out reports from low integrity mode.
635 //
636 // TODO(omaha): save reports from a low integrity process (specifically IE
637 // which does some extra special magic to thwart this) --
638 // possible by launch a process or a broker, etc.
639 if (vista::IsProcessProtected()) {
640 return;
641 }
642
643 if (data_.report_counts_num != 0) {
644 // Back the report IDs to the registry
645 __mutexBlock(this) {
646 ReportData *reports_in_config = NULL;
647 if (LoadReportData(&reports_in_config)) {
648 MergeReports(reports_in_config, &data_);
649 SaveReportData(reports_in_config);
650
651 byte *data = reinterpret_cast<byte*>(reports_in_config);
652 delete [] data;
653 } else {
654 // There's no data in the registry, so just fill it up with this
655 // component's data.
656 SaveReportData(&data_);
657 }
658 }
659 }
660 }
661
662 const TCHAR* const GetRegKeyShared() {
663 return vista_util::IsUserAdmin() ? _T("HKLM\\") kCiRegKeyShared :
664 _T("HKCU\\") kCiRegKeyShared;
665 }
666
667 void ReportIds::ResetReportsAfterPing() {
668 // We will lose reports from TRS between the time DebugReportString was called
669 // and now. We'll also lose reports from non TRS components if they exit in
670 // between this time. Not important.
671 data_.report_counts_num = 0;
672 __mutexBlock(this) {
673 RegKey::DeleteValue(GetRegKeyShared(), kRegValueReportIds);
674 }
675 }
676
677 void ReportIds::MergeReports(ReportData *data1, const ReportData *data2) {
678 // Loop through each report ID from data2. If we find it already, increment
679 // the report's count in data1. Otherwise, if there's enough space, add the
680 // report ID to data1.
681 uint32 i, j;
682 for (i = 0; i < data2->report_counts_num; ++i) {
683 bool duplicate_report = false;
684 for (j = 0; j < data1->report_counts_num; ++j) {
685 if (data1->report_ids[j] == data2->report_ids[i]) {
686 // uint16 is promoted to int.
687 data1->report_counts[j] = static_cast<uint16>(
688 data1->report_counts[j] + data2->report_counts[i]);
689 duplicate_report = true;
690 }
691 }
692
693 if (!duplicate_report && j < kMaxUniqueReports) {
694 data1->report_ids[j] = data2->report_ids[i];
695 data1->report_counts[j] = data2->report_counts[i];
696 data1->report_counts_num++;
697 }
698 }
699 }
700
701 bool ReportIds::LoadReportData(ReportData **data) {
702 DWORD byte_count = 0;
703 *data = NULL;
704 HRESULT hr = RegKey::GetValue(GetRegKeyShared(),
705 kRegValueReportIds,
706 reinterpret_cast<byte**>(data),
707 &byte_count);
708 if (SUCCEEDED(hr)) {
709 if (byte_count == sizeof(ReportData)) {
710 return true;
711 } else {
712 delete[] data;
713 data = NULL;
714 return false;
715 }
716 } else {
717 return false;
718 }
719 }
720
721 void ReportIds::SaveReportData(ReportData *data) {
722 if (data->report_counts_num) {
723 RegKey::SetValue(GetRegKeyShared(),
724 kRegValueReportIds,
725 reinterpret_cast<byte*>(data),
726 sizeof(ReportData));
727 }
728 }
729
730 bool ReportIds::ReleaseReport(uint32 id) {
731 uint32 max = data_.report_counts_num;
732
733 // If two threads call simultaneously, might miss one of an existing report
734 // here; not important.
735
736 uint32 i = 0;
737 for (i = 0; i < max; ++i) {
738 if (data_.report_ids[i] == id) {
739 data_.report_counts[i]++;
740 return true;
741 }
742 }
743
744 // If two threads call simultaneously, might overwrite first of another
745 // report; not important.
746
747 if (i < kMaxUniqueReports) {
748 data_.report_ids[i] = id;
749 data_.report_counts[i] = 1;
750 data_.report_counts_num = i + 1; // Set only after setting ids and count;
751 // don't use ++
752 }
753
754 #ifdef _DEBUG
755 OutputDebugString(SPRINTF(_T("release report %u\n"), id));
756 #endif
757 // must return true (return value of REPORT)
758 return true;
759 }
760
761 // caller deletes the string
762 TCHAR *ReportIds::DebugReportString() {
763 TCHAR *s = new TCHAR[(kMaxUniqueReports * kMaxReportCountString) + 1];
764 if (!s) { return NULL; }
765 s[0] = '\0';
766
767 #if 0
768 // this version if we use a hash table for the report counts:
769 uint32 id;
770 uint16 count;
771 hr = g_reports->First(&found, &id, &count);
772 while (SUCCEEDED(hr) && found) {
773 if (count) {
774 SafeCStringAppendFormat(&status_info, _T("%d=%d"),
775 id,
776 static_cast<uint32>(count));
777 }
778
779 hr = g_reports->Next(&found, &id, &count);
780 }
781 #endif
782
783 // The registry will contain the REPORTs from other components, so get that
784 // list and merge it with TRS'.
785 __mutexBlock(this) {
786 ReportData *reports_in_config = NULL;
787 if (LoadReportData(&reports_in_config)) {
788 MergeReports(&data_, reports_in_config);
789
790 byte *data = reinterpret_cast<byte*>(reports_in_config);
791 delete[] data;
792 }
793 }
794
795 TCHAR *current_pos = s;
796 for (uint32 i = 0; i < data_.report_counts_num; i++) {
797 if (data_.report_counts[i]) {
798 // should be no chance of overflow, ok to use wsprintf
799 int n = wsprintf(current_pos,
800 _T("%u:%u,"),
801 data_.report_ids[i],
802 static_cast<uint32>(data_.report_counts[i]));
803 current_pos += n;
804 }
805 }
806
807 return s;
808 }
809
810 // A simple helper function whose sole purpose is
811 // to isolate the dtor from SPRINTF which uses try/except.
812 // app_util::GetAppName returns a CString and dtor's
813 // aren't allowed in functions with try/except when
814 // the /EHsc flag is set.
815 void FillInSprintfErrorString(const TCHAR * format, TCHAR * error_string) {
816 wsprintf(error_string, L"SPRINTF buffer overrun %ls %hs %ls",
817 app_util::GetAppName(), omaha::GetVersionString(), format);
818 }
819
820 // following is currently included in release build
821 // return string from format+arglist; for debugging
822 TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) {
823 while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) {
824 // while (::InterlockedIncrement(&g_sprintf_interlock)>1) {
825 // ::InterlockedDecrement(&g_sprintf_interlock);
826 // Don't process APCs here.
827 // Can lead to infinite recursion, for example: filecap->logging->filecap...
828 Sleep(0);
829 }
830
831 g_current_sprintf_buffer++;
832 if (g_current_sprintf_buffer >= kSprintfBuffers) {
833 g_current_sprintf_buffer = 0;
834 }
835
836 TCHAR *sprintf_buf = NULL;
837
838 if (!g_initialized_sprintf) { // initialize buffers
839 g_sprintf_buffer = new TCHAR[
840 ((kSprintfMaxLen + 1) * kSprintfBuffers) +
841 kSprintfBufferOverrunPadding];
842 TCHAR* buffer = g_sprintf_buffer;
843 if (!buffer) { goto cleanup; }
844 for (int i = 0; i < kSprintfBuffers; ++i) {
845 g_sprintf_buffers[i] = buffer;
846 buffer += kSprintfMaxLen + 1;
847 }
848
849 #if !SHIPPING
850 for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers);
851 i < ((kSprintfMaxLen + 1) * kSprintfBuffers) +
852 kSprintfBufferOverrunPadding;
853 ++i) {
854 g_sprintf_buffer[i] = 1;
855 }
856 #endif
857
858 // InitializeCriticalSection(&g_sprintf_critical_section);
859 g_initialized_sprintf = true;
860 }
861
862 sprintf_buf = g_sprintf_buffers[g_current_sprintf_buffer];
863
864 // EnterCriticalSection(&g_sprintf_critical_section);
865
866 __try {
867 // create the formatted CString
868 va_list vl;
869 va_start(vl, format);
870 #ifdef DEBUG
871 StringCbVPrintfW(sprintf_buf, kSprintfMaxLen, format, vl);
872 #else
873 wvsprintfW(sprintf_buf, format, vl);
874 #endif
875 va_end(vl);
876
877 #if !SHIPPING
878 for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers);
879 i < ((kSprintfMaxLen+1) * kSprintfBuffers) +
880 kSprintfBufferOverrunPadding;
881 ++i) {
882 if (g_sprintf_buffer[i] != 1) {
883 TCHAR error_string[1024];
884 FillInSprintfErrorString(format, error_string);
885 MessageBox(NULL,
886 error_string,
887 error_string,
888 MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
889 break;
890 }
891 }
892 #endif
893 }
894 __except(EXCEPTION_EXECUTE_HANDLER) {
895 lstrcpyn(sprintf_buf, _T("sprintf failure"), kSprintfMaxLen);
896 }
897
898 // LeaveCriticalSection(&g_sprintf_critical_section);
899
900 cleanup:
901
902 ::InterlockedDecrement(&g_sprintf_interlock);
903 return sprintf_buf;
904 }
905
906 #if 0
907 TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) {
908 ASSERT(format, (L""));
909
910 g_current_sprintf_buffer++;
911 if (g_current_sprintf_buffer >= kSprintfBuffers) {
912 g_current_sprintf_buffer = 0;
913 }
914
915 TCHAR *sprintf_buf = sprintf_buffers[g_current_sprintf_buffer];
916 CFixedStringT<CString, kSprintfMaxLen> out;
917
918 va_list argptr;
919 va_start(argptr, format);
920 out.FormatV(format, argptr);
921 va_end(argptr);
922
923 // copy to fixed return buffers
924 SafeStrCat(sprintf_buf,
925 out.GetBufferSetLength(kSprintfMaxLen),
926 g_current_sprintf_buffer);
927 sprintf_buf[kSprintfMaxLen] = '\0';
928
929 return sprintf_buf;
930 }
931 #endif
932
933 // Cleanup allocated memory
934 class SprintfCleaner {
935 public:
936 SprintfCleaner() {}
937
938 ~SprintfCleaner() {
939 while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) {
940 Sleep(0);
941 }
942
943 if (g_initialized_sprintf) {
944 delete[] g_sprintf_buffer;
945 for (int i = 0; i < kSprintfBuffers; ++i) {
946 g_sprintf_buffers[i] = NULL;
947 }
948 g_initialized_sprintf = false;
949 }
950
951 ::InterlockedDecrement(&g_sprintf_interlock);
952 }
953
954 private:
955 DISALLOW_EVIL_CONSTRUCTORS(SprintfCleaner);
956 };
957
958 static SprintfCleaner cleaner;
959
960 // This is for our testers to find asserts in release mode.
961 #if !defined(_DEBUG) && defined(ASSERT_IN_RELEASE)
962 bool ReleaseAssert(const char *expr,
963 const TCHAR *msg,
964 const char *filename,
965 int32 linenumber) {
966 ASSERT(filename, (L""));
967 ASSERT(msg, (L""));
968 ASSERT(expr, (L""));
969
970 if (debug_assert_interceptor.interceptor()) {
971 // call replacement function (typically used for unit tests)
972 // Note that I'm doing this inside the in_assert block for paranoia;
973 // it's not really necessary and perhaps the wrong choice.
974 debug_assert_interceptor.interceptor()(expr,
975 CT2CA(msg),
976 filename,
977 linenumber);
978 return true;
979 }
980
981 OnAssert(expr, msg, filename, linenumber);
982
983 // Also put up a message box.
984 TCHAR error_string[1024] = {0};
985 wsprintf(error_string,
986 L"App: %ls\r\n"
987 L"Expr: %hs\r\n"
988 L"File: %hs\r\n"
989 L"Line: %d\r\n"
990 L"Version: %hs\r\n"
991 L"Message: ",
992 app_util::GetAppName(),
993 expr,
994 filename,
995 linenumber,
996 VER_TIMESTAMP_STR_FILE);
997 SafeStrCat(error_string, msg, arraysize(error_string));
998 SafeStrCat(error_string,
999 L"\r\n\r\n*** This message has been copied to the clipboard. ***",
1000 arraysize(error_string));
1001 SetClipboard(error_string);
1002
1003 TCHAR title_string[1024];
1004 wsprintf(title_string, L"%s ASSERT %s %hs",
1005 kAppName, app_util::GetAppName(), VER_TIMESTAMP_STR_FILE);
1006 MessageBox(NULL,
1007 error_string,
1008 title_string,
1009 MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
1010 return true;
1011 }
1012 #endif
1013
1014 #if defined(_DEBUG)
1015 void DebugAbort(const TCHAR *msg,
1016 const char* filename,
1017 int32 linenumber,
1018 bool do_abort) {
1019 DebugReport(0, R_FATAL, "", msg, filename, linenumber, DEBUGREPORT_ABORT);
1020 if (do_abort) {
1021 abort();
1022 }
1023 }
1024 #else
1025 void ReleaseAbort(const TCHAR *msg,
1026 const char* filename,
1027 int32 linenumber,
1028 bool do_abort) {
1029 // Send info to the server.
1030 #if defined(ASSERT_IN_RELEASE)
1031 OnAssert("", msg, filename, linenumber);
1032 #endif
1033
1034 // Also put up a message box.
1035 TCHAR error_string[1024] = {0};
1036 wsprintf(error_string,
1037 L"App: %ls\r\n"
1038 L"File: %hs\r\n"
1039 L"Line: %d\r\n"
1040 L"Version: %hs\r\n"
1041 L"Message: ",
1042 app_util::GetAppName(),
1043 filename,
1044 linenumber,
1045 omaha::GetVersionString());
1046 SafeStrCat(error_string, msg, arraysize(error_string));
1047 SafeStrCat(error_string,
1048 L"\r\n\r\n*** This message has been copied to the clipboard. ***",
1049 arraysize(error_string));
1050 SetClipboard(error_string);
1051
1052 TCHAR title_string[1024];
1053 wsprintf(title_string,
1054 L"%s ABORT %s %hs",
1055 kAppName,
1056 app_util::GetAppName(),
1057 omaha::GetVersionString());
1058 MessageBox(NULL,
1059 error_string,
1060 title_string,
1061 MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
1062
1063 if (do_abort) {
1064 abort();
1065 }
1066 }
1067 #endif
1068
1069
1070 #ifdef _DEBUG
1071
1072 void DumpInterface(IUnknown* unknown) {
1073 if (!unknown)
1074 return;
1075
1076 OutputDebugString(_T("------------------------------------------------\r\n"));
1077
1078 // Open the HKCR\Interfaces key where the IIDs of marshalable interfaces
1079 // are stored.
1080 RegKey key;
1081 if (SUCCEEDED(key.Open(HKEY_CLASSES_ROOT, _T("Interface"), KEY_READ))) {
1082 TCHAR name[_MAX_PATH + 1] = {0};
1083 DWORD name_size = _MAX_PATH;
1084 DWORD index = 0;
1085 FILETIME last_written;
1086
1087 //
1088 // Enumerate through the IIDs and see if the object supports it
1089 // by calling QueryInterface.
1090 //
1091 while (::RegEnumKeyEx(key.Key(),
1092 index++,
1093 name,
1094 &name_size,
1095 NULL,
1096 NULL,
1097 NULL,
1098 &last_written) == ERROR_SUCCESS) {
1099 // Convert the string to an IID
1100 IID iid;
1101 HRESULT hr = StringToGuidSafe(name, &iid);
1102
1103 CComPtr<IUnknown> test;
1104 if (unknown->QueryInterface(iid,
1105 reinterpret_cast<void**>(&test)) == S_OK) {
1106 //
1107 // The object supports this interface.
1108 // See if we can get a human readable name for the interface
1109 // If not, the name buffer already contains the string
1110 // representation of the IID, which we'll use as a fallback.
1111 //
1112 RegKey sub_key;
1113 if (sub_key.Open(key.Key(), name, KEY_READ) == S_OK) {
1114 scoped_array<TCHAR> display;
1115 // If this fails, we should still have the IID
1116 if (sub_key.GetValue(NULL, address(display)) == S_OK)
1117 lstrcpyn(name, display.get(), _MAX_PATH);
1118 }
1119
1120 CString fmt;
1121 SafeCStringFormat(&fmt, _T(" %s\r\n"), name);
1122 OutputDebugString(fmt);
1123 }
1124
1125 ZeroMemory(name, arraysize(name));
1126 name_size = _MAX_PATH;
1127 }
1128 }
1129
1130 OutputDebugString(_T("------------------------------------------------\r\n"));
1131 }
1132 #endif
1133
1134 // TODO(omaha): the implementation below is using CStrings so it is not very
1135 // conservative in terms of memory allocations.
1136 int SehNoMinidump(unsigned int code, struct _EXCEPTION_POINTERS *,
1137 const char *filename, int32 linenumber, bool show_message) {
1138 if (code == EXCEPTION_BREAKPOINT)
1139 return EXCEPTION_CONTINUE_SEARCH;
1140
1141 uint32 latest_cl = 0;
1142 #ifdef VERSION_LATEST_CL
1143 latest_cl = VERSION_LATEST_CL;
1144 #endif
1145
1146 if (show_message) {
1147 TCHAR message[1025] = {0};
1148 wsprintf(message,
1149 _T("Exception %x in %s %s %u\r\n\r\n%hs:%d\r\n"),
1150 code,
1151 app_util::GetAppName(),
1152 omaha::GetVersionString(),
1153 latest_cl,
1154 filename,
1155 linenumber);
1156
1157 SetClipboard(message);
1158 uint32 type = MB_ABORTRETRYIGNORE |
1159 MB_ICONERROR |
1160 MB_SERVICE_NOTIFICATION |
1161 MB_SETFOREGROUND |
1162 MB_TOPMOST;
1163 int ret = ::MessageBox(NULL, message, _T("Exception"), type);
1164 switch (ret) {
1165 case IDABORT:
1166 // Kamikaze if the user chose 'abort'
1167 ::ExitProcess(static_cast<UINT>(-1));
1168 break;
1169
1170 case IDRETRY:
1171 // Break if the user chose "retry"
1172 __debugbreak();
1173 break;
1174
1175 default:
1176 // By default we ignore the message
1177 break;
1178 }
1179 }
1180 return EXCEPTION_EXECUTE_HANDLER;
1181 }
1182
1183 CString GetDebugDirectory() {
1184 CString debug_dir;
1185 CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive"));
1186 if (!system_drive.IsEmpty()) {
1187 debug_dir += system_drive;
1188 debug_dir += L"\\";
1189 }
1190 debug_dir += kCiDebugDirectory;
1191 return debug_dir;
1192 }
1193
1194 } // namespace omaha
1195
OLDNEW
« no previous file with comments | « base/debug.h ('k') | base/debug_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698