Index: base/debug.cc |
diff --git a/base/debug.cc b/base/debug.cc |
deleted file mode 100644 |
index b0b53bda3b090c1919ddcf96592fcb18f6d3d5ca..0000000000000000000000000000000000000000 |
--- a/base/debug.cc |
+++ /dev/null |
@@ -1,1195 +0,0 @@ |
-// Copyright 2003-2009 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
-// ======================================================================== |
-// |
-// Debug functions |
- |
-#include "omaha/base/debug.h" |
- |
-#include <dbghelp.h> |
-#include <wtsapi32.h> |
-#include <atlstr.h> |
-#ifdef _DEBUG |
-#include <atlcom.h> |
-#define STRSAFE_NO_DEPRECATE |
-#include <strsafe.h> |
-#endif |
-#include <stdlib.h> |
-#include <signal.h> |
-#include "base/basictypes.h" |
-#include "base/scoped_ptr.h" |
-#include "omaha/base/app_util.h" |
-#include "omaha/base/clipboard.h" |
-#include "omaha/base/commontypes.h" |
-#include "omaha/base/constants.h" |
-#include "omaha/base/const_addresses.h" |
-#include "omaha/base/const_config.h" |
-#include "omaha/base/const_debug.h" |
-#include "omaha/base/const_timeouts.h" |
-#include "omaha/base/file.h" |
-#include "omaha/base/logging.h" |
-#include "omaha/base/module_utils.h" |
-#include "omaha/base/omaha_version.h" |
-#include "omaha/base/reg_key.h" |
-#include "omaha/base/safe_format.h" |
-#include "omaha/base/scope_guard.h" |
-#include "omaha/base/scoped_ptr_address.h" |
-#include "omaha/base/string.h" |
-#include "omaha/base/system.h" |
-#include "omaha/base/synchronized.h" |
-#include "omaha/base/time.h" |
-#include "omaha/base/utils.h" |
-#include "omaha/base/vistautil.h" |
-#include "omaha/base/vista_utils.h" |
- |
-namespace omaha { |
- |
-#ifdef _DEBUG |
-#define kSprintfBuffers (100) // number of buffers for SPRINTF |
-#else |
-#define kSprintfBuffers (3) // number of buffers for SPRINTF |
-#endif |
- |
-// pad SPRINTF buffer to check for overruns |
-#if SHIPPING |
-#define kSprintfBufferOverrunPadding 0 |
-#else // !SHIPPING |
-#ifdef DEBUG |
-#define kSprintfBufferOverrunPadding 20000 |
-#else |
-#define kSprintfBufferOverrunPadding 1024 |
-#endif // DEBUG |
-#endif // SHIPPING |
- |
-#ifdef _DEBUG |
-const TCHAR* const kErrorRequestToSendFormat = |
- _T("*** Please hit Ignore to continue and send error information to the ") |
- _T("%s team ***\n*** These details have been pasted to the clipboard ***"); |
- |
-// Max length of report summary string. |
-const int kMaxReportSummaryLen = 1024 * 100; |
-#endif // DEBUG |
- |
-#define kReportIdsLock kLockPrefix \ |
- _T("Report_Ids_Lock_57146B01-6A07-4b8d-A1D8-0C3AFC3B2F9B") |
- |
-SELECTANY bool g_always_assert = false; |
-SELECTANY TCHAR *g_additional_status_ping_info = NULL; |
- |
-#define kSprintfMaxLen (1024 + 2) // max length that wvsprintf writes is 1024 |
-static bool g_initialized_sprintf = false; |
-static volatile LONG g_sprintf_interlock = 0; |
-static int g_current_sprintf_buffer = 0; |
-static TCHAR *g_sprintf_buffer = NULL; |
-static TCHAR *g_sprintf_buffers[kSprintfBuffers]; |
-SELECTANY volatile LONG g_debugassertrecursioncheck = 0; |
-static int g_total_reports = 0; |
- |
-SELECTANY ReportIds g_report_ids; |
- |
-// Builds a full path name out of the given filename. If the filename is |
-// a relative path, it is appended to the debug directory. Otherwise, if the |
-// filename is a full path, it returns it as the full debug filename. |
-static CString MakeFullDebugFilename(const TCHAR *filename) { |
- CString full_name; |
- if (lstrlen(filename) <= 2 || filename[1] != _T(':')) { |
- full_name = GetDebugDirectory(); |
- full_name += L"\\"; |
- } |
- full_name += filename; |
- return full_name; |
-} |
- |
- |
-// Displays the assert box. Due to session isolation, MB_SERVICE_NOTIFICATION |
-// flag does not work for Vista services. In this case, use WTS to display |
-// a message box in the active console session. |
-void ShowAssertDialog(const TCHAR *message, const TCHAR *title) { |
- int ret = 0; |
- OSVERSIONINFOEX osviex = {sizeof(OSVERSIONINFOEX), 0}; |
- const bool is_vista_or_greater = |
- ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osviex)) && |
- osviex.dwMajorVersion >= 6; |
- bool is_system_process = false; |
- if (is_vista_or_greater && |
- SUCCEEDED(IsSystemProcess(&is_system_process)) && |
- is_system_process) { |
- DWORD session_id = System::WTSGetActiveConsoleSessionId(); |
- if (session_id == kInvalidSessionId) { |
- session_id = WTS_CURRENT_SESSION; |
- } |
- DWORD response = 0; |
- ::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, |
- session_id, |
- const_cast<TCHAR*>(title), |
- _tcslen(title) * sizeof(TCHAR), |
- const_cast<TCHAR*>(message), |
- _tcslen(message) * sizeof(TCHAR), |
- MB_ABORTRETRYIGNORE | MB_ICONERROR, |
- 0, |
- &response, |
- true); |
- ret = response; |
- } else { |
- ret = ::MessageBoxW(NULL, |
- message, |
- title, |
- MB_ABORTRETRYIGNORE | |
- MB_ICONERROR | |
- MB_SERVICE_NOTIFICATION); |
- } |
- |
- switch (ret) { |
- case IDABORT: |
- // Terminate the process if the user chose 'Abort'. Calling ExitProcess |
- // here results in calling the destructors for static objects which can |
- // result in deadlocks. |
- raise(SIGABRT); |
- break; |
- |
- case IDRETRY: |
- // Break if the user chose "Retry". |
- __debugbreak(); |
- break; |
- default: |
- // By default we ignore the message. |
- break; |
- } |
-} |
- |
-DebugObserver* g_debug_observer = NULL; |
- |
-// replaces the debug observer, returns the previous value. |
-DebugObserver* SetDebugObserver(DebugObserver* observer) { |
- DebugObserver* old_value = g_debug_observer; |
- g_debug_observer = observer; |
- return old_value; |
-} |
- |
-DebugObserver* PeekDebugObserver() { |
- return g_debug_observer; |
-} |
- |
-int SehSendMinidump(unsigned int code, |
- struct _EXCEPTION_POINTERS *ep, |
- time64 time_between_minidumps) { |
- if (code == EXCEPTION_BREAKPOINT) |
- return EXCEPTION_CONTINUE_SEARCH; |
- |
- if (::IsDebuggerPresent()) |
- return EXCEPTION_CONTINUE_SEARCH; |
- |
- OutputDebugString(L"**SehSendMinidump**\r\n"); |
- |
- if (g_debug_observer) { |
- return g_debug_observer->SehSendMinidump(code, ep, time_between_minidumps); |
- } |
- |
- return EXCEPTION_EXECUTE_HANDLER; |
-} |
- |
-#if defined(_DEBUG) || defined(ASSERT_IN_RELEASE) |
-CallInterceptor<DebugAssertFunctionType> debug_assert_interceptor; |
- |
-// Replaces the debug assert function; returns the old value. |
-DebugAssertFunctionType* ReplaceDebugAssertFunction( |
- DebugAssertFunctionType* replacement) { |
- return debug_assert_interceptor.ReplaceFunction(replacement); |
-} |
- |
-void OnAssert(const char *expr, const TCHAR *msg, |
- const char *filename, int32 linenumber) { |
- if (g_debug_observer) { |
- g_debug_observer->OnAssert(expr, msg, filename, linenumber); |
- } |
-} |
-#endif |
- |
-#if defined(_DEBUG) |
-CString OnDebugReport(uint32 id, |
- bool is_report, |
- ReportType type, |
- const char *expr, |
- const TCHAR *message, |
- const char *filename, |
- int32 linenumber, |
- DebugReportKind debug_report_kind) { |
- CString trace; |
- if (g_debug_observer) { |
- trace = g_debug_observer->OnDebugReport(id, is_report, |
- type, expr, message, filename, |
- linenumber, debug_report_kind); |
- } |
- return trace; |
-} |
- |
-void SendExceptionReport(const TCHAR *log_file, const TCHAR *filename, int line, |
- const TCHAR *type, uint32 id, bool offline) { |
- if (g_debug_observer) { |
- g_debug_observer->SendExceptionReport(log_file, filename, line, |
- type, id, offline); |
- } |
-} |
-#endif |
- |
- |
-#ifdef _DEBUG // won't compile since _CrtDbgReport isn't defined. |
- |
-#include <crtdbg.h> // NOLINT |
-static CString g_report_summary; |
- |
-// dump summary of reports on exit |
-SELECTANY ReportSummaryGenerator g_report_summary_generator; |
- |
-ReportSummaryGenerator::~ReportSummaryGenerator() { |
- DumpReportSummary(); |
-} |
- |
-void ReportSummaryGenerator::DumpReportSummary() { |
- if (g_total_reports) { |
- ::OutputDebugString(L"REPORT SUMMARY:\r\n"); |
- ::OutputDebugString(SPRINTF(L"%d total reports\r\n", g_total_reports)); |
- ::OutputDebugString(g_report_summary); |
- } else { |
- ::OutputDebugString(L"NO REPORTS!!\r\n"); |
- } |
-} |
- |
-TCHAR *ReportSummaryGenerator::GetReportSummary() { |
- TCHAR *s = new TCHAR[kMaxReportSummaryLen]; |
- if (s) { |
- s[0] = 0; |
- if (g_total_reports) { |
- SafeStrCat(s, L"REPORT SUMMARY:\r\n\r\n", kMaxReportSummaryLen); |
- SafeStrCat(s, |
- SPRINTF(L"%d total reports\r\n\r\n", g_total_reports), |
- kMaxReportSummaryLen); |
- SafeStrCat(s, |
- g_report_summary. |
- Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(), |
- kMaxReportSummaryLen); |
- CString report_string = g_report_ids.DebugReportString(); |
- ReplaceCString(report_string, L"&", L"\r\n"); |
- SafeStrCat(s, |
- report_string. |
- Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(), |
- kMaxReportSummaryLen); |
- } else { |
- SafeStrCat(s, L"NO REPORTS!!\r\n", kMaxReportSummaryLen); |
- } |
- } |
- |
- return s; |
-} |
- |
-static CAtlMap<CString, uint32> g_reports_done; |
- |
-#endif // _DEBUG |
- |
-#ifdef _DEBUG |
- |
-void TraceError(DWORD error) { |
- HLOCAL mem = NULL; |
- ::FormatMessage( |
- FORMAT_MESSAGE_ALLOCATE_BUFFER | |
- FORMAT_MESSAGE_FROM_SYSTEM | |
- FORMAT_MESSAGE_IGNORE_INSERTS, |
- static_cast<LPVOID>(_AtlBaseModule.GetResourceInstance()), |
- error, |
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language |
- reinterpret_cast<TCHAR*>(&mem), |
- 0, |
- NULL); |
- |
- TCHAR* str = reinterpret_cast<TCHAR*>(::LocalLock(mem)); |
- ::OutputDebugString(str); |
- REPORT(false, R_ERROR, (str), 3968294226); |
- ::LocalFree(mem); |
-} |
- |
-// ban ASSERT/VERIFY/REPORT to prevent recursion |
-#undef ASSERT |
-#undef VERIFY |
-#undef REPORT |
- |
-// TODO(omaha): fix static initialization order below. |
-// The initialization order of static variables is not deterministic per C++ |
-// standard and it depends completely on the compiler implementation. |
-// For VC++ compiler we are using, it seems to work as expected. |
-// One real fix is to put all definitons of static variables inside a class and |
-// define a boolean variable in this class to indicate that all necessary |
-// static initializations have been done. |
-// |
-// The follow definition is used to detect whether we get the exception |
-// during initializing static variables. If this is the case, DebugReport() |
-// will not function and will throw an exception because some of its refering |
-// static variables are not initialized yet (i.e. g_reports_done). |
-const int kTestInitStaticVariablesDoneValue = 1234; |
-struct TestInitStaticVariablesDone { |
- int value; |
- TestInitStaticVariablesDone() : value(kTestInitStaticVariablesDoneValue) {} |
-}; |
-static TestInitStaticVariablesDone test_var; |
- |
-bool DebugReport(unsigned int id, |
- ReportType type, |
- const char *expr, |
- const TCHAR *message, |
- const char *filename, |
- int linenumber, |
- DebugReportKind debug_report_kind) { |
- int recursion_count = ::InterlockedIncrement(&g_debugassertrecursioncheck); |
- ON_SCOPE_EXIT(::InterlockedDecrement, &g_debugassertrecursioncheck); |
- if (recursion_count > 1) { |
- ::OutputDebugString(_T("recursive debugreport skipped\n")); |
- return 1; |
- } |
- |
- if (debug_assert_interceptor.interceptor()) { |
- // call replacement function (typically used for unit tests) |
- // Note that I'm doing this inside the in_assert block for paranoia; |
- // it's not really necessary and perhaps the wrong choice. |
- debug_assert_interceptor.interceptor()(expr, CT2A(message), filename, |
- linenumber); |
- return true; |
- } |
- |
- |
- // Check whether we have already finished initializing all static variables |
- // needed for executing DebugReport(). If not, bail out. |
- if (test_var.value != kTestInitStaticVariablesDoneValue) { |
- CString debug_msg; |
- SafeCStringFormat(&debug_msg, _T("%hs:%d - %s - %S"), |
- filename, linenumber, message, expr); |
- debug_msg.Append(_T("\n\nException occurs while initializing ") |
- _T("static variables needed for DebugReport")); |
- ShowAssertDialog(debug_msg, _T("DebugReport")); |
- return true; |
- } |
- |
- bool is_assert = debug_report_kind == DEBUGREPORT_ASSERT; |
- bool is_report = debug_report_kind == DEBUGREPORT_REPORT; |
- bool is_abort = debug_report_kind == DEBUGREPORT_ABORT; |
- |
- if (is_report) |
- g_total_reports++; |
- |
- g_report_ids.ReleaseReport(id); |
- |
- if (type == R_FATAL) { |
- if (is_report) { |
- // Treat as ASSERT |
- is_report = false; |
- is_assert = true; |
- } |
- } |
- |
- bool always_assert = g_always_assert; |
- |
- if (always_assert) { |
- is_report = false; |
- is_assert = true; |
- } |
- |
- if (!message) { |
- message = _T(""); |
- } |
- |
- // log to debugger |
- TCHAR *debug_string; |
- // ::OutputDebugString(DEBUG_LOG_SEPARATOR); |
- ::OutputDebugString(is_report ? _T("REPORT: ") : |
- (is_assert ? _T("ASSERT: ") : _T("ABORT: "))); |
- |
- CFixedStringT<CString, 1024> proc_name = app_util::GetAppName(); |
- |
- // last %s now %s skip %d |
- const TCHAR* format = message && *message ? |
- _T("[%hs:%d][%hs][%s]") : _T("[%hs:%d][%hs]"); |
- debug_string = SPRINTF(format, filename, linenumber, expr, message); |
- |
- // String_Int64ToString(g_last_report_time, 10), |
- // String_Int64ToString(time, 10), skip_report)); |
- |
- // ::OutputDebugString(DEBUG_LOG_SEPARATOR); |
- // ::OutputDebugString(_T("\n")); |
- |
-#ifdef LOGGING |
- // Log the reports via the logging system to all loggers. |
- CString what = is_report ? _T("REPORT") : |
- is_assert ? _T("ASSERT") : _T("ABORT"); |
- LC_LOG(LC_LOGGING, LEVEL_ERROR, (_T("[%s]%s"), what, debug_string)); |
-#else |
- ::OutputDebugString(debug_string); |
- ::OutputDebugString(_T("\n")); |
-#endif |
- |
- // skip sending strack trace for duplicate reports |
- CString report_id; |
- SafeCStringFormat(&report_id, _T("%hs:%d"), filename, linenumber); |
- |
- uint32 prev_reports = 0; |
- if (g_reports_done.Lookup(report_id, prev_reports) && is_report) { |
- prev_reports++; |
- g_reports_done.SetAt(report_id, prev_reports); |
- ::OutputDebugString(SPRINTF(_T("skipping duplicate report %s %d\n"), |
- report_id.GetString(), |
- prev_reports)); |
- return 1; |
- } |
- |
- prev_reports++; |
- g_reports_done.SetAt(report_id, prev_reports); |
- |
- g_report_summary.Append(debug_string); |
- g_report_summary.Append(L" ("); |
- g_report_summary.Append(itostr(id)); |
- g_report_summary.Append(L")"); |
- g_report_summary.Append(L"\r\n"); |
- |
- // ::OutputDebugString(_T("log to file\n")); |
- |
- // log to file |
- CString path_name(MakeFullDebugFilename(kCiDebugLogFile)); |
- HANDLE h = CreateFile(path_name, |
- GENERIC_WRITE | GENERIC_READ, |
- FILE_SHARE_READ | FILE_SHARE_WRITE, |
- NULL, |
- OPEN_ALWAYS, |
- FILE_ATTRIBUTE_NORMAL, |
- NULL); |
- |
- HANDLE assert_file = INVALID_HANDLE_VALUE; |
- if (is_assert) { |
- path_name = MakeFullDebugFilename(kCiAssertOccurredFile); |
- assert_file = CreateFile(path_name, |
- GENERIC_WRITE, |
- 0, |
- 0, |
- OPEN_ALWAYS, |
- FILE_FLAG_WRITE_THROUGH, |
- NULL); |
- } |
- |
- HANDLE abort_file = INVALID_HANDLE_VALUE; |
- if (is_abort) { |
- path_name = MakeFullDebugFilename(kCiAbortOccurredFile); |
- abort_file = CreateFile(path_name, |
- GENERIC_WRITE, |
- 0, |
- 0, |
- OPEN_ALWAYS, |
- FILE_FLAG_WRITE_THROUGH, |
- NULL); |
- } |
- |
- if (h != INVALID_HANDLE_VALUE || |
- assert_file != INVALID_HANDLE_VALUE || |
- abort_file != INVALID_HANDLE_VALUE) { |
- // more convenient for now to have this in UTF8 |
- char *utf8_buffer = new char[(lstrlen(debug_string)*2) + 1]; |
- if (utf8_buffer) { |
- int conv_bytes = WideCharToMultiByte(CP_UTF8, |
- 0, |
- debug_string, |
- lstrlen(debug_string), |
- utf8_buffer, |
- (lstrlen(debug_string) * 2) + 1, |
- NULL, |
- NULL); |
- |
- if (conv_bytes) { |
- DWORD bytes_written; |
- BOOL result; |
- |
- if (h != INVALID_HANDLE_VALUE) { |
- SetFilePointer(h, 0, NULL, FILE_END); |
- result = ::WriteFile(h, |
- (LPCVOID)utf8_buffer, |
- conv_bytes, |
- &bytes_written, |
- NULL); |
- result = ::WriteFile(h, |
- (LPCVOID)DEBUG_LOG_SEPARATOR_CHAR, |
- strlen(DEBUG_LOG_SEPARATOR_CHAR), |
- &bytes_written, |
- NULL); |
- } |
- if (assert_file != INVALID_HANDLE_VALUE) { |
- result = ::WriteFile(assert_file, |
- (LPCVOID)utf8_buffer, |
- conv_bytes, |
- &bytes_written, |
- NULL); |
- } |
- if (abort_file != INVALID_HANDLE_VALUE) { |
- result = ::WriteFile(abort_file, |
- (LPCVOID)utf8_buffer, |
- conv_bytes, |
- &bytes_written, |
- NULL); |
- } |
- } |
- |
- delete [] utf8_buffer; |
- } |
- } |
- |
- if (h != INVALID_HANDLE_VALUE) { |
- ::CloseHandle(h); |
- } |
- if (assert_file != INVALID_HANDLE_VALUE) { |
- ::CloseHandle(assert_file); |
- } |
- if (abort_file != INVALID_HANDLE_VALUE) { |
- ::CloseHandle(abort_file); |
- } |
- |
- CString stack_trace = OnDebugReport(id, is_report, type, expr, message, |
- filename, linenumber, debug_report_kind); |
- |
- if (is_report) { |
- return 1; |
- } |
- |
- ::OutputDebugString(L"show assert dialog\r\n"); |
- ::OutputDebugString(stack_trace.GetString()); |
- |
- CString process_path; |
- GetModuleFileName(NULL, &process_path); |
- |
- static TCHAR clipboard_string[4096] = {0}; |
- lstrcpyn(clipboard_string, |
- SPRINTF(L"%ls (pid=%i)\r\n%hs:%d\r\n\r\n%hs\r\n%s\r\n\r\n", |
- process_path, |
- ::GetCurrentProcessId(), |
- filename, |
- linenumber, |
- expr, |
- message), |
- arraysize(clipboard_string)); |
- stack_trace = stack_trace.Left( |
- arraysize(clipboard_string) - lstrlen(clipboard_string) - 1); |
- SafeStrCat(clipboard_string, stack_trace, arraysize(clipboard_string)); |
- SetClipboard(clipboard_string); |
- |
- stack_trace = stack_trace.Left(kMaxStackTraceDialogLen); |
- |
- CString assert_text; |
- SafeCStringFormat(&assert_text, |
- _T("Assertion (%ls) failed!\r\n\r\nProcess: %d ") |
- _T("(0x%08X)\r\nThread %d (0x%08X)\r\nProgram: %ls\r\n") |
- _T("Version: %s\r\nFile: %hs\r\nLine: %d\r\n\r\n"), |
- debug_report_kind == DEBUGREPORT_ASSERT ? |
- L"Assert" : (debug_report_kind == DEBUGREPORT_ABORT ? |
- L"Abort" : L"Report"), |
- ::GetCurrentProcessId(), ::GetCurrentProcessId(), ::GetCurrentThreadId(), |
- ::GetCurrentThreadId(), process_path, omaha::GetVersionString(), filename, |
- linenumber); |
- |
- CString error_request_to_send; |
- SafeCStringFormat(&error_request_to_send, kErrorRequestToSendFormat, kAppName); |
- |
- if (lstrlen(message) > 0) { |
- SafeCStringAppendFormat(&assert_text, |
- _T("Expression: %hs\r\nMessage: %s\r\n\r\n%s\r\n\r\n%s"), |
- expr, |
- message, |
- stack_trace, |
- error_request_to_send); |
- } else { |
- SafeCStringAppendFormat(&assert_text, |
- _T("Expression: %hs\r\n\r\n%s\r\n\r\n%s"), |
- expr, stack_trace, error_request_to_send); |
- } |
- |
- ShowAssertDialog(assert_text, CString(filename)); |
- return 1; |
-} |
- |
-#endif // #ifdef _DEBUG |
- |
- |
-ReportIds::ReportIds() { |
- data_.report_counts_num = 0; |
- NamedObjectAttributes lock_attr; |
- GetNamedObjectAttributes(kReportIdsLock, |
- vista_util::IsUserAdmin(), |
- &lock_attr); |
- InitializeWithSecAttr(lock_attr.name, &lock_attr.sa); |
-} |
- |
-ReportIds::~ReportIds() { |
- // don't attempt to write out reports from low integrity mode. |
- // |
- // TODO(omaha): save reports from a low integrity process (specifically IE |
- // which does some extra special magic to thwart this) -- |
- // possible by launch a process or a broker, etc. |
- if (vista::IsProcessProtected()) { |
- return; |
- } |
- |
- if (data_.report_counts_num != 0) { |
- // Back the report IDs to the registry |
- __mutexBlock(this) { |
- ReportData *reports_in_config = NULL; |
- if (LoadReportData(&reports_in_config)) { |
- MergeReports(reports_in_config, &data_); |
- SaveReportData(reports_in_config); |
- |
- byte *data = reinterpret_cast<byte*>(reports_in_config); |
- delete [] data; |
- } else { |
- // There's no data in the registry, so just fill it up with this |
- // component's data. |
- SaveReportData(&data_); |
- } |
- } |
- } |
-} |
- |
-const TCHAR* const GetRegKeyShared() { |
- return vista_util::IsUserAdmin() ? _T("HKLM\\") kCiRegKeyShared : |
- _T("HKCU\\") kCiRegKeyShared; |
-} |
- |
-void ReportIds::ResetReportsAfterPing() { |
- // We will lose reports from TRS between the time DebugReportString was called |
- // and now. We'll also lose reports from non TRS components if they exit in |
- // between this time. Not important. |
- data_.report_counts_num = 0; |
- __mutexBlock(this) { |
- RegKey::DeleteValue(GetRegKeyShared(), kRegValueReportIds); |
- } |
-} |
- |
-void ReportIds::MergeReports(ReportData *data1, const ReportData *data2) { |
- // Loop through each report ID from data2. If we find it already, increment |
- // the report's count in data1. Otherwise, if there's enough space, add the |
- // report ID to data1. |
- uint32 i, j; |
- for (i = 0; i < data2->report_counts_num; ++i) { |
- bool duplicate_report = false; |
- for (j = 0; j < data1->report_counts_num; ++j) { |
- if (data1->report_ids[j] == data2->report_ids[i]) { |
- // uint16 is promoted to int. |
- data1->report_counts[j] = static_cast<uint16>( |
- data1->report_counts[j] + data2->report_counts[i]); |
- duplicate_report = true; |
- } |
- } |
- |
- if (!duplicate_report && j < kMaxUniqueReports) { |
- data1->report_ids[j] = data2->report_ids[i]; |
- data1->report_counts[j] = data2->report_counts[i]; |
- data1->report_counts_num++; |
- } |
- } |
-} |
- |
-bool ReportIds::LoadReportData(ReportData **data) { |
- DWORD byte_count = 0; |
- *data = NULL; |
- HRESULT hr = RegKey::GetValue(GetRegKeyShared(), |
- kRegValueReportIds, |
- reinterpret_cast<byte**>(data), |
- &byte_count); |
- if (SUCCEEDED(hr)) { |
- if (byte_count == sizeof(ReportData)) { |
- return true; |
- } else { |
- delete[] data; |
- data = NULL; |
- return false; |
- } |
- } else { |
- return false; |
- } |
-} |
- |
-void ReportIds::SaveReportData(ReportData *data) { |
- if (data->report_counts_num) { |
- RegKey::SetValue(GetRegKeyShared(), |
- kRegValueReportIds, |
- reinterpret_cast<byte*>(data), |
- sizeof(ReportData)); |
- } |
-} |
- |
-bool ReportIds::ReleaseReport(uint32 id) { |
- uint32 max = data_.report_counts_num; |
- |
- // If two threads call simultaneously, might miss one of an existing report |
- // here; not important. |
- |
- uint32 i = 0; |
- for (i = 0; i < max; ++i) { |
- if (data_.report_ids[i] == id) { |
- data_.report_counts[i]++; |
- return true; |
- } |
- } |
- |
- // If two threads call simultaneously, might overwrite first of another |
- // report; not important. |
- |
- if (i < kMaxUniqueReports) { |
- data_.report_ids[i] = id; |
- data_.report_counts[i] = 1; |
- data_.report_counts_num = i + 1; // Set only after setting ids and count; |
- // don't use ++ |
- } |
- |
-#ifdef _DEBUG |
- OutputDebugString(SPRINTF(_T("release report %u\n"), id)); |
-#endif |
- // must return true (return value of REPORT) |
- return true; |
-} |
- |
-// caller deletes the string |
-TCHAR *ReportIds::DebugReportString() { |
- TCHAR *s = new TCHAR[(kMaxUniqueReports * kMaxReportCountString) + 1]; |
- if (!s) { return NULL; } |
- s[0] = '\0'; |
- |
-#if 0 |
- // this version if we use a hash table for the report counts: |
- uint32 id; |
- uint16 count; |
- hr = g_reports->First(&found, &id, &count); |
- while (SUCCEEDED(hr) && found) { |
- if (count) { |
- SafeCStringAppendFormat(&status_info, _T("%d=%d"), |
- id, |
- static_cast<uint32>(count)); |
- } |
- |
- hr = g_reports->Next(&found, &id, &count); |
- } |
-#endif |
- |
- // The registry will contain the REPORTs from other components, so get that |
- // list and merge it with TRS'. |
- __mutexBlock(this) { |
- ReportData *reports_in_config = NULL; |
- if (LoadReportData(&reports_in_config)) { |
- MergeReports(&data_, reports_in_config); |
- |
- byte *data = reinterpret_cast<byte*>(reports_in_config); |
- delete[] data; |
- } |
- } |
- |
- TCHAR *current_pos = s; |
- for (uint32 i = 0; i < data_.report_counts_num; i++) { |
- if (data_.report_counts[i]) { |
- // should be no chance of overflow, ok to use wsprintf |
- int n = wsprintf(current_pos, |
- _T("%u:%u,"), |
- data_.report_ids[i], |
- static_cast<uint32>(data_.report_counts[i])); |
- current_pos += n; |
- } |
- } |
- |
- return s; |
-} |
- |
-// A simple helper function whose sole purpose is |
-// to isolate the dtor from SPRINTF which uses try/except. |
-// app_util::GetAppName returns a CString and dtor's |
-// aren't allowed in functions with try/except when |
-// the /EHsc flag is set. |
-void FillInSprintfErrorString(const TCHAR * format, TCHAR * error_string) { |
- wsprintf(error_string, L"SPRINTF buffer overrun %ls %hs %ls", |
- app_util::GetAppName(), omaha::GetVersionString(), format); |
-} |
- |
-// following is currently included in release build |
-// return string from format+arglist; for debugging |
-TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) { |
- while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) { |
- // while (::InterlockedIncrement(&g_sprintf_interlock)>1) { |
- // ::InterlockedDecrement(&g_sprintf_interlock); |
- // Don't process APCs here. |
- // Can lead to infinite recursion, for example: filecap->logging->filecap... |
- Sleep(0); |
- } |
- |
- g_current_sprintf_buffer++; |
- if (g_current_sprintf_buffer >= kSprintfBuffers) { |
- g_current_sprintf_buffer = 0; |
- } |
- |
- TCHAR *sprintf_buf = NULL; |
- |
- if (!g_initialized_sprintf) { // initialize buffers |
- g_sprintf_buffer = new TCHAR[ |
- ((kSprintfMaxLen + 1) * kSprintfBuffers) + |
- kSprintfBufferOverrunPadding]; |
- TCHAR* buffer = g_sprintf_buffer; |
- if (!buffer) { goto cleanup; } |
- for (int i = 0; i < kSprintfBuffers; ++i) { |
- g_sprintf_buffers[i] = buffer; |
- buffer += kSprintfMaxLen + 1; |
- } |
- |
-#if !SHIPPING |
- for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers); |
- i < ((kSprintfMaxLen + 1) * kSprintfBuffers) + |
- kSprintfBufferOverrunPadding; |
- ++i) { |
- g_sprintf_buffer[i] = 1; |
- } |
-#endif |
- |
- // InitializeCriticalSection(&g_sprintf_critical_section); |
- g_initialized_sprintf = true; |
- } |
- |
- sprintf_buf = g_sprintf_buffers[g_current_sprintf_buffer]; |
- |
- // EnterCriticalSection(&g_sprintf_critical_section); |
- |
- __try { |
- // create the formatted CString |
- va_list vl; |
- va_start(vl, format); |
-#ifdef DEBUG |
- StringCbVPrintfW(sprintf_buf, kSprintfMaxLen, format, vl); |
-#else |
- wvsprintfW(sprintf_buf, format, vl); |
-#endif |
- va_end(vl); |
- |
-#if !SHIPPING |
- for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers); |
- i < ((kSprintfMaxLen+1) * kSprintfBuffers) + |
- kSprintfBufferOverrunPadding; |
- ++i) { |
- if (g_sprintf_buffer[i] != 1) { |
- TCHAR error_string[1024]; |
- FillInSprintfErrorString(format, error_string); |
- MessageBox(NULL, |
- error_string, |
- error_string, |
- MB_OK | MB_SETFOREGROUND | MB_TOPMOST); |
- break; |
- } |
- } |
-#endif |
- } |
- __except(EXCEPTION_EXECUTE_HANDLER) { |
- lstrcpyn(sprintf_buf, _T("sprintf failure"), kSprintfMaxLen); |
- } |
- |
- // LeaveCriticalSection(&g_sprintf_critical_section); |
- |
- cleanup: |
- |
- ::InterlockedDecrement(&g_sprintf_interlock); |
- return sprintf_buf; |
-} |
- |
-#if 0 |
- TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) { |
- ASSERT(format, (L"")); |
- |
- g_current_sprintf_buffer++; |
- if (g_current_sprintf_buffer >= kSprintfBuffers) { |
- g_current_sprintf_buffer = 0; |
- } |
- |
- TCHAR *sprintf_buf = sprintf_buffers[g_current_sprintf_buffer]; |
- CFixedStringT<CString, kSprintfMaxLen> out; |
- |
- va_list argptr; |
- va_start(argptr, format); |
- out.FormatV(format, argptr); |
- va_end(argptr); |
- |
- // copy to fixed return buffers |
- SafeStrCat(sprintf_buf, |
- out.GetBufferSetLength(kSprintfMaxLen), |
- g_current_sprintf_buffer); |
- sprintf_buf[kSprintfMaxLen] = '\0'; |
- |
- return sprintf_buf; |
-} |
-#endif |
- |
-// Cleanup allocated memory |
-class SprintfCleaner { |
- public: |
- SprintfCleaner() {} |
- |
- ~SprintfCleaner() { |
- while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) { |
- Sleep(0); |
- } |
- |
- if (g_initialized_sprintf) { |
- delete[] g_sprintf_buffer; |
- for (int i = 0; i < kSprintfBuffers; ++i) { |
- g_sprintf_buffers[i] = NULL; |
- } |
- g_initialized_sprintf = false; |
- } |
- |
- ::InterlockedDecrement(&g_sprintf_interlock); |
- } |
- |
- private: |
- DISALLOW_EVIL_CONSTRUCTORS(SprintfCleaner); |
-}; |
- |
-static SprintfCleaner cleaner; |
- |
-// This is for our testers to find asserts in release mode. |
-#if !defined(_DEBUG) && defined(ASSERT_IN_RELEASE) |
-bool ReleaseAssert(const char *expr, |
- const TCHAR *msg, |
- const char *filename, |
- int32 linenumber) { |
- ASSERT(filename, (L"")); |
- ASSERT(msg, (L"")); |
- ASSERT(expr, (L"")); |
- |
- if (debug_assert_interceptor.interceptor()) { |
- // call replacement function (typically used for unit tests) |
- // Note that I'm doing this inside the in_assert block for paranoia; |
- // it's not really necessary and perhaps the wrong choice. |
- debug_assert_interceptor.interceptor()(expr, |
- CT2CA(msg), |
- filename, |
- linenumber); |
- return true; |
- } |
- |
- OnAssert(expr, msg, filename, linenumber); |
- |
- // Also put up a message box. |
- TCHAR error_string[1024] = {0}; |
- wsprintf(error_string, |
- L"App: %ls\r\n" |
- L"Expr: %hs\r\n" |
- L"File: %hs\r\n" |
- L"Line: %d\r\n" |
- L"Version: %hs\r\n" |
- L"Message: ", |
- app_util::GetAppName(), |
- expr, |
- filename, |
- linenumber, |
- VER_TIMESTAMP_STR_FILE); |
- SafeStrCat(error_string, msg, arraysize(error_string)); |
- SafeStrCat(error_string, |
- L"\r\n\r\n*** This message has been copied to the clipboard. ***", |
- arraysize(error_string)); |
- SetClipboard(error_string); |
- |
- TCHAR title_string[1024]; |
- wsprintf(title_string, L"%s ASSERT %s %hs", |
- kAppName, app_util::GetAppName(), VER_TIMESTAMP_STR_FILE); |
- MessageBox(NULL, |
- error_string, |
- title_string, |
- MB_OK | MB_SETFOREGROUND | MB_TOPMOST); |
- return true; |
-} |
-#endif |
- |
-#if defined(_DEBUG) |
-void DebugAbort(const TCHAR *msg, |
- const char* filename, |
- int32 linenumber, |
- bool do_abort) { |
- DebugReport(0, R_FATAL, "", msg, filename, linenumber, DEBUGREPORT_ABORT); |
- if (do_abort) { |
- abort(); |
- } |
-} |
-#else |
-void ReleaseAbort(const TCHAR *msg, |
- const char* filename, |
- int32 linenumber, |
- bool do_abort) { |
- // Send info to the server. |
-#if defined(ASSERT_IN_RELEASE) |
- OnAssert("", msg, filename, linenumber); |
-#endif |
- |
- // Also put up a message box. |
- TCHAR error_string[1024] = {0}; |
- wsprintf(error_string, |
- L"App: %ls\r\n" |
- L"File: %hs\r\n" |
- L"Line: %d\r\n" |
- L"Version: %hs\r\n" |
- L"Message: ", |
- app_util::GetAppName(), |
- filename, |
- linenumber, |
- omaha::GetVersionString()); |
- SafeStrCat(error_string, msg, arraysize(error_string)); |
- SafeStrCat(error_string, |
- L"\r\n\r\n*** This message has been copied to the clipboard. ***", |
- arraysize(error_string)); |
- SetClipboard(error_string); |
- |
- TCHAR title_string[1024]; |
- wsprintf(title_string, |
- L"%s ABORT %s %hs", |
- kAppName, |
- app_util::GetAppName(), |
- omaha::GetVersionString()); |
- MessageBox(NULL, |
- error_string, |
- title_string, |
- MB_OK | MB_SETFOREGROUND | MB_TOPMOST); |
- |
- if (do_abort) { |
- abort(); |
- } |
-} |
-#endif |
- |
- |
-#ifdef _DEBUG |
- |
-void DumpInterface(IUnknown* unknown) { |
- if (!unknown) |
- return; |
- |
- OutputDebugString(_T("------------------------------------------------\r\n")); |
- |
- // Open the HKCR\Interfaces key where the IIDs of marshalable interfaces |
- // are stored. |
- RegKey key; |
- if (SUCCEEDED(key.Open(HKEY_CLASSES_ROOT, _T("Interface"), KEY_READ))) { |
- TCHAR name[_MAX_PATH + 1] = {0}; |
- DWORD name_size = _MAX_PATH; |
- DWORD index = 0; |
- FILETIME last_written; |
- |
- // |
- // Enumerate through the IIDs and see if the object supports it |
- // by calling QueryInterface. |
- // |
- while (::RegEnumKeyEx(key.Key(), |
- index++, |
- name, |
- &name_size, |
- NULL, |
- NULL, |
- NULL, |
- &last_written) == ERROR_SUCCESS) { |
- // Convert the string to an IID |
- IID iid; |
- HRESULT hr = StringToGuidSafe(name, &iid); |
- |
- CComPtr<IUnknown> test; |
- if (unknown->QueryInterface(iid, |
- reinterpret_cast<void**>(&test)) == S_OK) { |
- // |
- // The object supports this interface. |
- // See if we can get a human readable name for the interface |
- // If not, the name buffer already contains the string |
- // representation of the IID, which we'll use as a fallback. |
- // |
- RegKey sub_key; |
- if (sub_key.Open(key.Key(), name, KEY_READ) == S_OK) { |
- scoped_array<TCHAR> display; |
- // If this fails, we should still have the IID |
- if (sub_key.GetValue(NULL, address(display)) == S_OK) |
- lstrcpyn(name, display.get(), _MAX_PATH); |
- } |
- |
- CString fmt; |
- SafeCStringFormat(&fmt, _T(" %s\r\n"), name); |
- OutputDebugString(fmt); |
- } |
- |
- ZeroMemory(name, arraysize(name)); |
- name_size = _MAX_PATH; |
- } |
- } |
- |
- OutputDebugString(_T("------------------------------------------------\r\n")); |
-} |
-#endif |
- |
-// TODO(omaha): the implementation below is using CStrings so it is not very |
-// conservative in terms of memory allocations. |
-int SehNoMinidump(unsigned int code, struct _EXCEPTION_POINTERS *, |
- const char *filename, int32 linenumber, bool show_message) { |
- if (code == EXCEPTION_BREAKPOINT) |
- return EXCEPTION_CONTINUE_SEARCH; |
- |
- uint32 latest_cl = 0; |
-#ifdef VERSION_LATEST_CL |
- latest_cl = VERSION_LATEST_CL; |
-#endif |
- |
- if (show_message) { |
- TCHAR message[1025] = {0}; |
- wsprintf(message, |
- _T("Exception %x in %s %s %u\r\n\r\n%hs:%d\r\n"), |
- code, |
- app_util::GetAppName(), |
- omaha::GetVersionString(), |
- latest_cl, |
- filename, |
- linenumber); |
- |
- SetClipboard(message); |
- uint32 type = MB_ABORTRETRYIGNORE | |
- MB_ICONERROR | |
- MB_SERVICE_NOTIFICATION | |
- MB_SETFOREGROUND | |
- MB_TOPMOST; |
- int ret = ::MessageBox(NULL, message, _T("Exception"), type); |
- switch (ret) { |
- case IDABORT: |
- // Kamikaze if the user chose 'abort' |
- ::ExitProcess(static_cast<UINT>(-1)); |
- break; |
- |
- case IDRETRY: |
- // Break if the user chose "retry" |
- __debugbreak(); |
- break; |
- |
- default: |
- // By default we ignore the message |
- break; |
- } |
- } |
- return EXCEPTION_EXECUTE_HANDLER; |
-} |
- |
-CString GetDebugDirectory() { |
- CString debug_dir; |
- CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive")); |
- if (!system_drive.IsEmpty()) { |
- debug_dir += system_drive; |
- debug_dir += L"\\"; |
- } |
- debug_dir += kCiDebugDirectory; |
- return debug_dir; |
-} |
- |
-} // namespace omaha |
- |