diff options
author | deanm@google.com <deanm@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-13 11:15:11 +0000 |
---|---|---|
committer | deanm@google.com <deanm@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-13 11:15:11 +0000 |
commit | 1ffe08c10b4ade6acf92d4a1c2f604200facd410 (patch) | |
tree | cb979005f9ee0db87102b9ef67564fbe654b9e67 /base | |
parent | 21d610fb26b275f489dacbd6c372ddf55323ea55 (diff) | |
download | chromium_src-1ffe08c10b4ade6acf92d4a1c2f604200facd410.zip chromium_src-1ffe08c10b4ade6acf92d4a1c2f604200facd410.tar.gz chromium_src-1ffe08c10b4ade6acf92d4a1c2f604200facd410.tar.bz2 |
Make debug_util cross platform, adding BeingDebugged and BreakDebugger. Linux
supported added, and Mac OSX left as a todo for the mac team.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@785 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/SConscript | 2 | ||||
-rw-r--r-- | base/build/base.vcproj | 4 | ||||
-rw-r--r-- | base/debug_util.cc | 98 | ||||
-rw-r--r-- | base/debug_util.h | 16 | ||||
-rw-r--r-- | base/debug_util_posix.cc | 90 | ||||
-rw-r--r-- | base/debug_util_win.cc | 127 | ||||
-rw-r--r-- | base/logging.cc | 24 |
7 files changed, 249 insertions, 112 deletions
diff --git a/base/SConscript b/base/SConscript index 40ae21a..f645697 100644 --- a/base/SConscript +++ b/base/SConscript @@ -123,6 +123,7 @@ if env['PLATFORM'] == 'win32': 'base_paths_win.cc', 'clipboard_win.cc', 'condition_variable_win.cc', + 'debug_util_win.cc', 'file_util_win.cc', 'iat_patch.cc', 'lock_impl_win.cc', @@ -139,6 +140,7 @@ if env['PLATFORM'] == 'win32': if env['PLATFORM'] in ('darwin', 'posix'): input_files.extend([ 'condition_variable_posix.cc', + 'debug_util_posix.cc', 'lock_impl_posix.cc', 'shared_memory_posix.cc', 'thread_local_storage_posix.cc', diff --git a/base/build/base.vcproj b/base/build/base.vcproj index 428eb1e..b4a886cb 100644 --- a/base/build/base.vcproj +++ b/base/build/base.vcproj @@ -258,6 +258,10 @@ > </File> <File + RelativePath="..\debug_util_win.cc" + > + </File> + <File RelativePath="..\event_recorder.cc" > </File> diff --git a/base/debug_util.cc b/base/debug_util.cc index fb60f5a..1679650 100644 --- a/base/debug_util.cc +++ b/base/debug_util.cc @@ -27,106 +27,20 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include <windows.h> - -#include "base/basictypes.h" #include "base/debug_util.h" -namespace { - -// Minimalist key reader. -// Note: Does not use the CRT. -bool RegReadString(HKEY root, const wchar_t* subkey, - const wchar_t* value_name, wchar_t* buffer, int* len) { - HKEY key = NULL; - DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); - if (ERROR_SUCCESS != res || key == NULL) - return false; - - DWORD type = 0; - DWORD buffer_size = *len * sizeof(wchar_t); - // We don't support REG_EXPAND_SZ. - res = RegQueryValueEx(key, value_name, NULL, &type, - reinterpret_cast<BYTE*>(buffer), &buffer_size); - if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { - // Make sure the buffer is NULL terminated. - buffer[*len - 1] = 0; - *len = lstrlen(buffer); - RegCloseKey(key); - return true; - } - RegCloseKey(key); - return false; -} - -// Replaces each "%ld" in input per a value. Not efficient but it works. -// Note: Does not use the CRT. -bool StringReplace(const wchar_t* input, int value, wchar_t* output, - int output_len) { - memset(output, 0, output_len*sizeof(wchar_t)); - int input_len = lstrlen(input); - - for (int i = 0; i < input_len; ++i) { - int current_output_len = lstrlen(output); - - if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { - // Make sure we have enough place left. - if ((current_output_len + 12) >= output_len) - return false; - - // Cheap _itow(). - wsprintf(output+current_output_len, L"%d", value); - i += 2; - } else { - if (current_output_len >= output_len) - return false; - output[current_output_len] = input[i]; - } - } - return true; -} - -} // namespace - -// Note: Does not use the CRT. -bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { - wchar_t reg_value[1026]; - int len = arraysize(reg_value); - if (RegReadString(HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", - L"Debugger", reg_value, &len)) { - wchar_t command_line[1026]; - if (StringReplace(reg_value, process_id, command_line, - arraysize(command_line))) { - // We don't mind if the debugger is present because it will simply fail - // to attach to this process. - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - PROCESS_INFORMATION process_info = {0}; +#include <windows.h> - if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, - &startup_info, &process_info)) { - CloseHandle(process_info.hThread); - WaitForInputIdle(process_info.hProcess, 10000); - CloseHandle(process_info.hProcess); - return true; - } - } - } - return false; -} +#include "base/platform_thread.h" -// Note: Does not use the CRT. bool DebugUtil::WaitForDebugger(int wait_seconds, bool silent) { for (int i = 0; i < wait_seconds * 10; ++i) { - if (IsDebuggerPresent()) { - if (!silent) { - // If you hit here, you just use -debug-on-start or -debug-children. - __debugbreak(); - } + if (BeingDebugged()) { + if (!silent) + BreakDebugger(); return true; } - Sleep(100); + PlatformThread::Sleep(100); } return false; } diff --git a/base/debug_util.h b/base/debug_util.h index 54381e5..288ea4b 100644 --- a/base/debug_util.h +++ b/base/debug_util.h @@ -27,8 +27,12 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#ifndef BASE_DEBUG_UTIL_H__ -#define BASE_DEBUG_UTIL_H__ +// This is a cross platform interface for helper functions related to debuggers. +// You should use this to test if you're running under a debugger, and if you +// would like to yield (breakpoint) into the debugger. + +#ifndef BASE_DEBUG_UTIL_H_ +#define BASE_DEBUG_UTIL_H_ class DebugUtil { public: @@ -39,6 +43,12 @@ class DebugUtil { // Waits wait_seconds seconds for a debugger to attach to the current process. // When silent is false, an exception is thrown when a debugger is detected. static bool WaitForDebugger(int wait_seconds, bool silent); + + // Are we running under a debugger? + static bool BeingDebugged(); + + // Break into the debugger, assumes a debugger is present. + static void BreakDebugger(); }; -#endif // BASE_DEBUG_UTIL_H__ +#endif // BASE_DEBUG_UTIL_H_ diff --git a/base/debug_util_posix.cc b/base/debug_util_posix.cc new file mode 100644 index 0000000..84b5aae --- /dev/null +++ b/base/debug_util_posix.cc @@ -0,0 +1,90 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/debug_util.h" + +#include <unistd.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "base/notimplemented.h" +#include "base/string_piece.h" + +bool DebugUtil::SpawnDebuggerOnProcess(unsigned /* process_id */) { + NOTIMPLEMENTED(); + return false; +} + +#if defined(OS_MACOSX) +// http://developer.apple.com/qa/qa2004/qa1361.html +bool DebugUtil::BeingDebugged() { + NOTIMPLEMENTED(); + return false; +} + +#elif defined(OS_LINUX) +// We can look in /proc/self/status for TracerPid. We are likely used in crash +// handling, so we are careful not to use the heap or have side effects. +// Another option that is common is to try to ptrace yourself, but then we +// can't detach without forking(), and that's not so great. +bool DebugUtil::BeingDebugged() { + int status_fd = open("/proc/self/status", O_RDONLY); + if (status_fd == -1) + return false; + + // We assume our line will be in the first 1024 characters and that we can + // read this much all at once. In practice this will generally be true. + // This simplifies and speeds up things considerably. + char buf[1024]; + + ssize_t num_read = read(status_fd, buf, sizeof(buf)); + close(status_fd); + + if (num_read <= 0) + return false; + + StringPiece status(buf, num_read); + StringPiece tracer("TracerPid:\t"); + + StringPiece::size_type pid_index = status.find(tracer); + if (pid_index == StringPiece::npos) + return false; + + // Our pid is 0 without a debugger, assume this for any pid starting with 0. + pid_index += tracer.size(); + return pid_index < status.size() && status[pid_index] != '0'; +} +#endif + +// static +void DebugUtil::BreakDebugger() { + asm ("int3"); +} diff --git a/base/debug_util_win.cc b/base/debug_util_win.cc new file mode 100644 index 0000000..0f67a16 --- /dev/null +++ b/base/debug_util_win.cc @@ -0,0 +1,127 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/debug_util.h" + +namespace { + +// Minimalist key reader. +// Note: Does not use the CRT. +bool RegReadString(HKEY root, const wchar_t* subkey, + const wchar_t* value_name, wchar_t* buffer, int* len) { + HKEY key = NULL; + DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); + if (ERROR_SUCCESS != res || key == NULL) + return false; + + DWORD type = 0; + DWORD buffer_size = *len * sizeof(wchar_t); + // We don't support REG_EXPAND_SZ. + res = RegQueryValueEx(key, value_name, NULL, &type, + reinterpret_cast<BYTE*>(buffer), &buffer_size); + if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { + // Make sure the buffer is NULL terminated. + buffer[*len - 1] = 0; + *len = lstrlen(buffer); + RegCloseKey(key); + return true; + } + RegCloseKey(key); + return false; +} + +// Replaces each "%ld" in input per a value. Not efficient but it works. +// Note: Does not use the CRT. +bool StringReplace(const wchar_t* input, int value, wchar_t* output, + int output_len) { + memset(output, 0, output_len*sizeof(wchar_t)); + int input_len = lstrlen(input); + + for (int i = 0; i < input_len; ++i) { + int current_output_len = lstrlen(output); + + if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { + // Make sure we have enough place left. + if ((current_output_len + 12) >= output_len) + return false; + + // Cheap _itow(). + wsprintf(output+current_output_len, L"%d", value); + i += 2; + } else { + if (current_output_len >= output_len) + return false; + output[current_output_len] = input[i]; + } + } + return true; +} + +} // namespace + +// Note: Does not use the CRT. +bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { + wchar_t reg_value[1026]; + int len = arraysize(reg_value); + if (RegReadString(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", + L"Debugger", reg_value, &len)) { + wchar_t command_line[1026]; + if (StringReplace(reg_value, process_id, command_line, + arraysize(command_line))) { + // We don't mind if the debugger is present because it will simply fail + // to attach to this process. + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + PROCESS_INFORMATION process_info = {0}; + + if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, + &startup_info, &process_info)) { + CloseHandle(process_info.hThread); + WaitForInputIdle(process_info.hProcess, 10000); + CloseHandle(process_info.hProcess); + return true; + } + } + } + return false; +} + +// static +bool DebugUtil::BeingDebugged() { + return ::IsDebuggerPresent() != 0; +} + +// static +void DebugUtil::BreakDebugger() { + __debugbreak(); +} diff --git a/base/logging.cc b/base/logging.cc index 2648346..1e20dd0 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -59,6 +59,7 @@ typedef pthread_mutex_t* MutexHandle; #include <algorithm> #include "base/base_switches.h" #include "base/command_line.h" +#include "base/debug_util.h" #include "base/lock_impl.h" #include "base/logging.h" #include "base/string_util.h" @@ -473,13 +474,9 @@ LogMessage::~LogMessage() { if (severity_ == LOG_FATAL) { // display a message or break into the debugger on a fatal error -#if defined(OS_WIN) - if (::IsDebuggerPresent()) { - __debugbreak(); - } - else -#endif - { + if (DebugUtil::BeingDebugged()) { + DebugUtil::BreakDebugger(); + } else { if (log_assert_handler) { // make a copy of the string for the handler out of paranoia log_assert_handler(std::string(stream_.str())); @@ -488,16 +485,9 @@ LogMessage::~LogMessage() { // the debug message process DisplayDebugMessage(stream_.str()); // Crash the process to generate a dump. -#if defined(OS_WIN) - __debugbreak(); -#elif defined(OS_POSIX) -#if defined(OS_MACOSX) - // TODO: when we have breakpad support, generate a breakpad dump, but - // until then, do not invoke the Apple crash reporter. - Debugger(); -#endif - exit(-1); -#endif + DebugUtil::BreakDebugger(); + // TODO(mmentovai): when we have breakpad support, generate a breakpad + // dump, but until then, do not invoke the Apple crash reporter. } } } |