| OLD | NEW |
| (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 | |
| OLD | NEW |