diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym new file mode 100644 index 000000000000..e042a5ec42d2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym @@ -0,0 +1,3 @@ +MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so +PUBLIC 400 0 __kernel_vsyscall +STACK WIN 4 400 100 1 1 0 0 0 0 0 1 diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym new file mode 100644 index 000000000000..c209c2375640 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym @@ -0,0 +1,3 @@ +MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so +PUBLIC 400 0 __kernel_vsyscall +STACK WIN 4 400 200 3 3 0 0 0 0 0 1 \ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc index d336c1e268ac..1fe7e83d9567 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc @@ -1,8 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2009, Google Inc. // All rights reserved. // -// Author: Li Liu -// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,49 +27,88 @@ // (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 -#include -#include -#include -#include +// The ExceptionHandler object installs signal handlers for a number of +// signals. We rely on the signal handler running on the thread which crashed +// in order to identify it. This is true of the synchronous signals (SEGV etc), +// but not true of ABRT. Thus, if you send ABRT to yourself in a program which +// uses ExceptionHandler, you need to use tgkill to direct it to the current +// thread. +// +// The signal flow looks like this: +// +// SignalHandler (uses a global stack of ExceptionHandler objects to find +// | one to handle the signal. If the first rejects it, try +// | the second etc...) +// V +// HandleSignal ----------------------------| (clones a new process which +// | | shares an address space with +// (wait for cloned | the crashed process. This +// process) | allows us to ptrace the crashed +// | | process) +// V V +// (set signal handler to ThreadEntry (static function to bounce +// SIG_DFL and rethrow, | back into the object) +// killing the crashed | +// process) V +// DoDump (writes minidump) +// | +// V +// sys_exit +// -#include -#include -#include -#include -#include +// This code is a little fragmented. Different functions of the ExceptionHandler +// class run in a number of different contexts. Some of them run in a normal +// context and are easy to code, others run in a compromised context and the +// restrictions at the top of minidump_writer.cc apply: no libc and use the +// alternative malloc. Each function should have comment above it detailing the +// context which it runs in. #include "client/linux/handler/exception_handler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" +#include "common/linux/memory.h" +#include "client/linux/minidump_writer//minidump_writer.h" #include "common/linux/guid_creator.h" -#include "google_breakpad/common/minidump_format.h" + +// A wrapper for the tgkill syscall: send a signal to a specific thread. +static int tgkill(pid_t tgid, pid_t tid, int sig) { + syscall(__NR_tgkill, tgid, tid, sig); + return 0; +} namespace google_breakpad { -// Signals that we are interested. -int SigTable[] = { -#if defined(SIGSEGV) - SIGSEGV, -#endif -#ifdef SIGABRT - SIGABRT, -#endif -#ifdef SIGFPE - SIGFPE, -#endif -#ifdef SIGILL - SIGILL, -#endif -#ifdef SIGBUS - SIGBUS, -#endif +// The list of signals which we consider to be crashes. The default action for +// all these signals must be Core (see man 7 signal) because we rethrow the +// signal after handling it and expect that it'll be fatal. +static const int kExceptionSignals[] = { + SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1 }; -std::vector *ExceptionHandler::handler_stack_ = NULL; -int ExceptionHandler::handler_stack_index_ = 0; +// We can stack multiple exception handlers. In that case, this is the global +// which holds the stack. +std::vector* ExceptionHandler::handler_stack_ = NULL; +unsigned ExceptionHandler::handler_stack_index_ = 0; pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = -PTHREAD_MUTEX_INITIALIZER; + PTHREAD_MUTEX_INITIALIZER; -ExceptionHandler::ExceptionHandler(const string &dump_path, +// Runs before crashing: normal context. +ExceptionHandler::ExceptionHandler(const std::string &dump_path, FilterCallback filter, MinidumpCallback callback, void *callback_context, @@ -80,212 +117,77 @@ ExceptionHandler::ExceptionHandler(const string &dump_path, callback_(callback), callback_context_(callback_context), dump_path_(), - installed_handler_(install_handler) { + handler_installed_(install_handler), + crash_handler_(NULL) { set_dump_path(dump_path); - act_.sa_handler = HandleException; - act_.sa_flags = SA_ONSTACK; - sigemptyset(&act_.sa_mask); - // now, make sure we're blocking all the signals we are handling - // when we're handling any of them - for ( size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) { - sigaddset(&act_.sa_mask, SigTable[i]); - } - if (install_handler) { - SetupHandler(); + InstallHandlers(); + pthread_mutex_lock(&handler_stack_mutex_); - if (handler_stack_ == NULL) - handler_stack_ = new std::vector; - handler_stack_->push_back(this); + if (handler_stack_ == NULL) + handler_stack_ = new std::vector; + handler_stack_->push_back(this); pthread_mutex_unlock(&handler_stack_mutex_); } } +// Runs before crashing: normal context. ExceptionHandler::~ExceptionHandler() { - TeardownAllHandler(); - pthread_mutex_lock(&handler_stack_mutex_); - if (handler_stack_->back() == this) { - handler_stack_->pop_back(); - } else { - fprintf(stderr, "warning: removing Breakpad handler out of order\n"); - for (std::vector::iterator iterator = - handler_stack_->begin(); - iterator != handler_stack_->end(); - ++iterator) { - if (*iterator == this) { - handler_stack_->erase(iterator); - } - } - } - - if (handler_stack_->empty()) { - // When destroying the last ExceptionHandler that installed a handler, - // clean up the handler stack. - delete handler_stack_; - handler_stack_ = NULL; - } - pthread_mutex_unlock(&handler_stack_mutex_); + UninstallHandlers(); } -bool ExceptionHandler::WriteMinidump() { - bool success = InternalWriteMinidump(0, 0, NULL); - UpdateNextID(); - return success; -} +// Runs before crashing: normal context. +bool ExceptionHandler::InstallHandlers() { + // We run the signal handlers on an alternative stack because we might have + // crashed because of a stack overflow. -// static -bool ExceptionHandler::WriteMinidump(const string &dump_path, - MinidumpCallback callback, - void *callback_context) { - ExceptionHandler handler(dump_path, NULL, callback, - callback_context, false); - return handler.InternalWriteMinidump(0, 0, NULL); -} + // We use this value rather than SIGSTKSZ because we would end up overrunning + // such a small stack. + static const unsigned kSigStackSize = 8192; -void ExceptionHandler::SetupHandler() { - // Signal on a different stack to avoid using the stack - // of the crashing thread. - struct sigaltstack sig_stack; - sig_stack.ss_sp = malloc(MINSIGSTKSZ); - if (sig_stack.ss_sp == NULL) - return; - sig_stack.ss_size = MINSIGSTKSZ; - sig_stack.ss_flags = 0; + signal_stack = malloc(kSigStackSize); + stack_t stack; + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = signal_stack; + stack.ss_size = kSigStackSize; - if (sigaltstack(&sig_stack, NULL) < 0) - return; - for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) - SetupHandler(SigTable[i]); -} - -void ExceptionHandler::SetupHandler(int signo) { - - // We're storing pointers to the old signal action - // structure, rather than copying the structure - // because we can't count on the sa_mask field to - // be scalar. - struct sigaction *old_act = &old_actions_[signo]; - - if (sigaction(signo, &act_, old_act) < 0) - return; -} - -void ExceptionHandler::TeardownHandler(int signo) { - TeardownHandler(signo, NULL); -} - -void ExceptionHandler::TeardownHandler(int signo, struct sigaction *final_handler) { - if (old_actions_[signo].sa_handler) { - struct sigaction *act = &old_actions_[signo]; - sigaction(signo, act, final_handler); - memset(&old_actions_[signo], 0x0, sizeof(struct sigaction)); - } -} - -void ExceptionHandler::TeardownAllHandler() { - for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) { - TeardownHandler(SigTable[i]); - } -} - -// static -void ExceptionHandler::HandleException(int signo) { - // In Linux, the context information about the signal is put on the stack of - // the signal handler frame as value parameter. For some reasons, the - // prototype of the handler doesn't declare this information as parameter, we - // will do it by hand. It is the second parameter above the signal number. - // However, if we are being called by another signal handler passing the - // signal up the chain, then we may not have this random extra parameter, - // so we may have to walk the stack to find it. We do the actual work - // on another thread, where it's a little safer, but we want the ebp - // from this frame to find it. - uintptr_t current_ebp = 0; - asm volatile ("movl %%ebp, %0" - :"=m"(current_ebp)); - - pthread_mutex_lock(&handler_stack_mutex_); - ExceptionHandler *current_handler = - handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); - pthread_mutex_unlock(&handler_stack_mutex_); - - // Restore original handler. - struct sigaction old_action; - current_handler->TeardownHandler(signo, &old_action); - - struct sigcontext *sig_ctx = NULL; - if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) { - // Fully handled this exception, safe to exit. - exit(EXIT_FAILURE); - } else { - // Exception not fully handled, will call the next handler in stack to - // process it. - if (old_action.sa_handler != NULL && sig_ctx != NULL) { - - // Have our own typedef, because of the comment above w.r.t signal - // context on the stack - typedef void (*SignalHandler)(int signo, struct sigcontext); - - SignalHandler old_handler = - reinterpret_cast(old_action.sa_handler); - - sigset_t old_set; - // Use SIG_BLOCK here because we don't want to unblock a signal - // that the signal handler we're currently in needs to block - sigprocmask(SIG_BLOCK, &old_action.sa_mask, &old_set); - old_handler(signo, *sig_ctx); - sigprocmask(SIG_SETMASK, &old_set, NULL); - } - - } - - pthread_mutex_lock(&handler_stack_mutex_); - current_handler->SetupHandler(signo); - --handler_stack_index_; - // All the handlers in stack have been invoked to handle the exception, - // normally the process should be terminated and should not reach here. - // In case we got here, ask the OS to handle it to avoid endless loop, - // normally the OS will generate a core and termiate the process. This - // may be desired to debug the program. - if (handler_stack_index_ == 0) - signal(signo, SIG_DFL); - pthread_mutex_unlock(&handler_stack_mutex_); -} - -bool ExceptionHandler::InternalWriteMinidump(int signo, - uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) { - if (filter_ && !filter_(callback_context_)) + if (sigaltstack(&stack, NULL) == -1) return false; - bool success = false; - // Block all the signals we want to process when writting minidump. - // We don't want it to be interrupted. - sigset_t sig_blocked, sig_old; - bool blocked = true; - sigfillset(&sig_blocked); - for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) - sigdelset(&sig_blocked, SigTable[i]); - if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) { - blocked = false; - fprintf(stderr, "google_breakpad::ExceptionHandler::HandleException: " - "failed to block signals.\n"); + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + + // mask all exception signals when we're handling one of them. + for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) + sigaddset(&sa.sa_mask, kExceptionSignals[i]); + + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO; + + for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) { + struct sigaction* old = new struct sigaction; + if (sigaction(kExceptionSignals[i], &sa, old) == -1) + return false; + old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old)); } - - success = minidump_generator_.WriteMinidumpToFile( - next_minidump_path_c_, signo, sighandler_ebp, sig_ctx); - - // Unblock the signals. - if (blocked) { - sigprocmask(SIG_SETMASK, &sig_old, NULL); - } - - if (callback_) - success = callback_(dump_path_c_, next_minidump_id_c_, - callback_context_, success); - return success; + return true; } +// Runs before crashing: normal context. +void ExceptionHandler::UninstallHandlers() { + for (unsigned i = 0; i < old_handlers_.size(); ++i) { + struct sigaction *action = + reinterpret_cast(old_handlers_[i].second); + sigaction(old_handlers_[i].first, action, NULL); + delete action; + } + + old_handlers_.clear(); +} + +// Runs before crashing: normal context. void ExceptionHandler::UpdateNextID() { GUID guid; char guid_str[kGUIDStringLength + 1]; @@ -303,4 +205,120 @@ void ExceptionHandler::UpdateNextID() { } } +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +// static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { + // All the exception signals are blocked at this point. + + pthread_mutex_lock(&handler_stack_mutex_); + + if (!handler_stack_->size()) { + pthread_mutex_unlock(&handler_stack_mutex_); + return; + } + + for (int i = handler_stack_->size() - 1; i >= 0; --i) { + if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) { + // successfully handled: We are in an invalid state since an exception + // signal has been delivered. We don't call the exit handlers because + // they could end up corrupting on-disk state. + break; + } + } + + pthread_mutex_unlock(&handler_stack_mutex_); + + // Terminate ourselves with the same signal so that our parent knows that we + // crashed. The default action for all the signals which we catch is Core, so + // this is the end of us. + signal(sig, SIG_DFL); + tgkill(getpid(), sys_gettid(), sig); + + // not reached. +} + +struct ThreadArgument { + pid_t pid; // the crashing process + ExceptionHandler* handler; + const void* context; // a CrashContext structure + size_t context_size; +}; + +// This is the entry function for the cloned process. We are in a compromised +// context here: see the top of the file. +// static +int ExceptionHandler::ThreadEntry(void *arg) { + const ThreadArgument *thread_arg = reinterpret_cast(arg); + return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, + thread_arg->context_size) == false; +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { + if (filter_ && !filter_(callback_context_)) + return false; + + // Allow ourselves to be dumped. + sys_prctl(PR_SET_DUMPABLE, 1); + + CrashContext context; + memcpy(&context.siginfo, info, sizeof(siginfo_t)); + memcpy(&context.context, uc, sizeof(struct ucontext)); + memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs, + sizeof(context.float_state)); + context.tid = sys_gettid(); + + if (crash_handler_ && crash_handler_(&context, sizeof(context), + callback_context_)) + return true; + + static const unsigned kChildStackSize = 8000; + PageAllocator allocator; + uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); + if (!stack) + return false; + // clone() needs the top-most address. (scrub just to be safe) + stack += kChildStackSize; + my_memset(stack - 16, 0, 16); + + ThreadArgument thread_arg; + thread_arg.handler = this; + thread_arg.pid = getpid(); + thread_arg.context = &context; + thread_arg.context_size = sizeof(context); + + const pid_t child = sys_clone( + ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, + &thread_arg, NULL, NULL, NULL); + int r, status; + do { + r = sys_waitpid(child, &status, __WALL); + } while (r == -1 && errno == EINTR); + + if (r == -1) { + static const char msg[] = "ExceptionHandler::HandleSignal: waitpid failed:"; + sys_write(2, msg, sizeof(msg) - 1); + sys_write(2, strerror(errno), strlen(strerror(errno))); + sys_write(2, "\n", 1); + } + + bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; + + if (callback_) + success = callback_(dump_path_c_, next_minidump_id_c_, + callback_context_, success); + + return success; +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the cloned process. +bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, + size_t context_size) { + return google_breakpad::WriteMinidump( + next_minidump_path_c_, crashing_process, context, context_size); +} + } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h index 6ea09a11084c..b579a6a98110 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h @@ -1,8 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2009, Google Inc. // All rights reserved. // -// Author: Li Liu -// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,26 +27,16 @@ // (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 CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ -#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ +#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ +#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ -#include - -#include -#include -#include #include +#include -#include "client/linux/handler/minidump_generator.h" - -// Context information when exception occured. -struct sigcontex; +#include namespace google_breakpad { -using std::string; - -// // ExceptionHandler // // ExceptionHandler can write a minidump file when an exception occurs, @@ -73,7 +61,6 @@ using std::string; // // Caller should try to make the callbacks as crash-friendly as possible, // it should avoid use heap memory allocation as much as possible. -// class ExceptionHandler { public: // A callback function to run before Breakpad performs any substantial @@ -108,6 +95,15 @@ class ExceptionHandler { void *context, bool succeeded); + // In certain cases, a user may wish to handle the generation of the minidump + // themselves. In this case, they can install a handler callback which is + // called when a crash has occured. If this function returns true, no other + // processing of occurs and the process will shortly be crashed. If this + // returns false, the normal processing continues. + typedef bool (*HandlerCallback)(const void* crash_context, + size_t crash_context_size, + void* context); + // Creates a new ExceptionHandler instance to handle writing minidumps. // Before writing a minidump, the optional filter callback will be called. // Its return value determines whether or not Breakpad should write a @@ -116,111 +112,87 @@ class ExceptionHandler { // If install_handler is true, then a minidump will be written whenever // an unhandled exception occurs. If it is false, minidumps will only // be written when WriteMinidump is called. - ExceptionHandler(const string &dump_path, + ExceptionHandler(const std::string &dump_path, FilterCallback filter, MinidumpCallback callback, void *callback_context, bool install_handler); ~ExceptionHandler(); // Get and set the minidump path. - string dump_path() const { return dump_path_; } - void set_dump_path(const string &dump_path) { + std::string dump_path() const { return dump_path_; } + void set_dump_path(const std::string &dump_path) { dump_path_ = dump_path; dump_path_c_ = dump_path_.c_str(); UpdateNextID(); } + void set_crash_handler(HandlerCallback callback) { + crash_handler_ = callback; + } + // Writes a minidump immediately. This can be used to capture the // execution state independently of a crash. Returns true on success. bool WriteMinidump(); // Convenience form of WriteMinidump which does not require an // ExceptionHandler instance. - static bool WriteMinidump(const string &dump_path, + static bool WriteMinidump(const std::string &dump_path, MinidumpCallback callback, void *callback_context); + // This structure is passed to minidump_writer.h:WriteMinidump via an opaque + // blob. It shouldn't be needed in any user code. + struct CrashContext { + siginfo_t siginfo; + pid_t tid; // the crashing thread. + struct ucontext context; + struct _libc_fpstate float_state; + }; + private: - // Setup crash handler. - void SetupHandler(); - // Setup signal handler for a signal. - void SetupHandler(int signo); - // Teardown the handler for a signal. - void TeardownHandler(int signo); - // Teardown the handler for a signal. - void TeardownHandler(int signo, struct sigaction *old); - // Teardown all handlers. - void TeardownAllHandler(); + bool InstallHandlers(); + void UninstallHandlers(); + void PreresolveSymbols(); - // Signal handler. - static void HandleException(int signo); - - // If called from a signal handler, sighandler_ebp is the ebp of - // that signal handler's frame, and sig_ctx is an out parameter - // that will be set to point at the sigcontext that was placed - // on the stack by the kernel. You can pass zero and NULL - // for the second and third parameters if you are not calling - // this from a signal handler. - bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx); - - // Generates a new ID and stores it in next_minidump_id, and stores the - // path of the next minidump to be written in next_minidump_path_. void UpdateNextID(); + static void SignalHandler(int sig, siginfo_t* info, void* uc); + bool HandleSignal(int sig, siginfo_t* info, void* uc); + static int ThreadEntry(void* arg); + bool DoDump(pid_t crashing_process, const void* context, + size_t context_size); - private: - FilterCallback filter_; - MinidumpCallback callback_; - void *callback_context_; + const FilterCallback filter_; + const MinidumpCallback callback_; + void* const callback_context_; - // The directory in which a minidump will be written, set by the dump_path - // argument to the constructor, or set_dump_path. - string dump_path_; - - // The basename of the next minidump to be written, without the extension - string next_minidump_id_; - - // The full pathname of the next minidump to be written, including the file - // extension - string next_minidump_path_; + std::string dump_path_; + std::string next_minidump_path_; + std::string next_minidump_id_; // Pointers to C-string representations of the above. These are set // when the above are set so we can avoid calling c_str during // an exception. - const char *dump_path_c_; - const char *next_minidump_id_c_; - const char *next_minidump_path_c_; + const char* dump_path_c_; + const char* next_minidump_path_c_; + const char* next_minidump_id_c_; - // True if the ExceptionHandler installed an unhandled exception filter - // when created (with an install_handler parameter set to true). - bool installed_handler_; + const bool handler_installed_; + void* signal_stack; // the handler stack. + HandlerCallback crash_handler_; // The global exception handler stack. This is need becuase there may exist // multiple ExceptionHandler instances in a process. Each will have itself // registered in this stack. - static std::vector *handler_stack_; + static std::vector *handler_stack_; // The index of the handler that should handle the next exception. - static int handler_stack_index_; + static unsigned handler_stack_index_; static pthread_mutex_t handler_stack_mutex_; - // The minidump generator. - MinidumpGenerator minidump_generator_; - - // disallow copy ctor and operator= - explicit ExceptionHandler(const ExceptionHandler &); - void operator=(const ExceptionHandler &); - - // The sigactions structure we use for each signal - struct sigaction act_; - - - // Keep the previous handlers for the signal. - // We're wasting a bit of memory here since we only change - // the handler for some signals but i want to avoid allocating - // memory in the signal handler - struct sigaction old_actions_[NSIG]; + // A vector of the old signal handlers. The void* is a pointer to a newly + // allocated sigaction structure to avoid pulling in too many includes. + std::vector > old_handlers_; }; } // namespace google_breakpad -#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ +#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc deleted file mode 100644 index 2d94553ddfb4..000000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// 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 -#include - -#include -#include -#include -#include - -#include "client/linux/handler/exception_handler.h" -#include "client/linux/handler/linux_thread.h" - -using namespace google_breakpad; - -// Thread use this to see if it should stop working. -static bool should_exit = false; - -static int foo2(int arg) { - // Stack variable, used for debugging stack dumps. - /*DDDebug*/printf("%s:%d\n", __FUNCTION__, __LINE__); - int c = 0xcccccccc; - fprintf(stderr, "Thread trying to crash: %x\n", getpid()); - c = *reinterpret_cast(0x5); - return c; -} - -static int foo(int arg) { - // Stack variable, used for debugging stack dumps. - int b = 0xbbbbbbbb; - b = foo2(b); - return b; -} - -static void *thread_crash(void *) { - // Stack variable, used for debugging stack dumps. - int a = 0xaaaaaaaa; - sleep(1); - a = foo(a); - printf("%x\n", a); - return NULL; -} - -static void *thread_main(void *) { - while (!should_exit) - sleep(1); - return NULL; -} - -static void CreateCrashThread() { - pthread_t h; - pthread_create(&h, NULL, thread_crash, NULL); - pthread_detach(h); -} - -// Create working threads. -static void CreateThread(int num) { - pthread_t h; - for (int i = 0; i < num; ++i) { - pthread_create(&h, NULL, thread_main, NULL); - pthread_detach(h); - } -} - -// Callback when minidump written. -static bool MinidumpCallback(const char *dump_path, - const char *minidump_id, - void *context, - bool succeeded) { - int index = reinterpret_cast(context); - printf("%d %s: %s is dumped\n", index, __FUNCTION__, minidump_id); - if (index == 0) { - should_exit = true; - return true; - } - // Don't process it. - return false; -} - -int main(int argc, char *argv[]) { - int handler_index = 0; - ExceptionHandler handler_ignore(".", NULL, MinidumpCallback, - (void*)handler_index, true); - ++handler_index; - ExceptionHandler handler_process(".", NULL, MinidumpCallback, - (void*)handler_index, true); - CreateCrashThread(); - CreateThread(10); - - while (true) - sleep(1); - should_exit = true; - - return 0; -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc new file mode 100644 index 000000000000..2f4e10454f1b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc @@ -0,0 +1,256 @@ +// Copyright (c) 2009, 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 + +#include +#include +#include +#include +#include +#include + +#include "client/linux/handler//exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" +#include "breakpad_googletest_includes.h" + +// This provides a wrapper around system calls which may be +// interrupted by a signal and return EINTR. See man 7 signal. +#define HANDLE_EINTR(x) ({ \ + typeof(x) __eintr_result__; \ + do { \ + __eintr_result__ = x; \ + } while (__eintr_result__ == -1 && errno == EINTR); \ + __eintr_result__;\ +}) + +using namespace google_breakpad; + +static void sigchld_handler(int signo) { } + +class ExceptionHandlerTest : public ::testing::Test { + protected: + void SetUp() { + // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigchld_handler; + ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); + } + + void TearDown() { + sigaction(SIGCHLD, &old_action, NULL); + } + + struct sigaction old_action; +}; + +TEST(ExceptionHandlerTest, Simple) { + ExceptionHandler handler("/tmp", NULL, NULL, NULL, true); +} + +static bool DoneCallback(const char* dump_path, + const char* minidump_id, + void* context, + bool succeeded) { + if (!succeeded) + return succeeded; + + int fd = (intptr_t) context; + uint32_t len = my_strlen(minidump_id); + HANDLE_EINTR(sys_write(fd, &len, sizeof(len))); + HANDLE_EINTR(sys_write(fd, minidump_id, len)); + sys_close(fd); + + return true; +} + +TEST(ExceptionHandlerTest, ChildCrash) { + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1], + true); + *reinterpret_cast(NULL) = 0; + } + close(fds[1]); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGSEGV); + + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(r, 1); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len)); + ASSERT_LT(len, 2048); + char* filename = reinterpret_cast(malloc(len + 1)); + ASSERT_EQ(read(fds[0], filename, len), len); + filename[len] = 0; + close(fds[0]); + + const std::string minidump_filename = std::string("/tmp/") + filename + + ".dmp"; + + struct stat st; + ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(minidump_filename.c_str()); +} + +static const unsigned kControlMsgSize = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); + +static bool +CrashHandler(const void* crash_context, size_t crash_context_size, + void* context) { + const int fd = (intptr_t) context; + int fds[2]; + pipe(fds); + + struct kernel_msghdr msg = {0}; + struct kernel_iovec iov; + iov.iov_base = const_cast(crash_context); + iov.iov_len = crash_context_size; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + char cmsg[kControlMsgSize]; + memset(cmsg, 0, kControlMsgSize); + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + *((int*) CMSG_DATA(hdr)) = fds[1]; + hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_CREDENTIALS; + hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + struct ucred *cred = reinterpret_cast(CMSG_DATA(hdr)); + cred->uid = getuid(); + cred->gid = getgid(); + cred->pid = getpid(); + + HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); + sys_close(fds[1]); + + char b; + HANDLE_EINTR(sys_read(fds[0], &b, 1)); + + return true; +} + +TEST(ExceptionHandlerTest, ExternalDumper) { + int fds[2]; + ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); + static const int on = 1; + setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true); + handler.set_crash_handler(CrashHandler); + *reinterpret_cast(NULL) = 0; + } + + close(fds[1]); + struct msghdr msg = {0}; + struct iovec iov; + static const unsigned kCrashContextSize = + sizeof(ExceptionHandler::CrashContext); + char context[kCrashContextSize]; + char control[kControlMsgSize]; + iov.iov_base = context; + iov.iov_len = kCrashContextSize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); + ASSERT_EQ(n, kCrashContextSize); + ASSERT_EQ(msg.msg_controllen, kControlMsgSize); + ASSERT_EQ(msg.msg_flags, 0); + + pid_t crashing_pid = -1; + int signal_fd = -1; + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const unsigned len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + ASSERT_EQ(len, sizeof(int)); + signal_fd = *((int *) CMSG_DATA(hdr)); + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + const struct ucred *cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + + ASSERT_NE(crashing_pid, -1); + ASSERT_NE(signal_fd, -1); + + char templ[] = "/tmp/exception-handler-unittest-XXXXXX"; + mktemp(templ); + ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context, + kCrashContextSize)); + static const char b = 0; + HANDLE_EINTR(write(signal_fd, &b, 1)); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGSEGV); + + struct stat st; + ASSERT_EQ(stat(templ, &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(templ); +} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc deleted file mode 100644 index c8ac492690d7..000000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "client/linux/handler/linux_thread.h" - -using namespace google_breakpad; - -// This unamed namespace contains helper function. -namespace { - -// Context information for the callbacks when validating address by listing -// modules. -struct AddressValidatingContext { - uintptr_t address; - bool is_mapped; - - AddressValidatingContext() : address(0UL), is_mapped(false) { - } -}; - -// Convert from string to int. -bool LocalAtoi(char *s, int *r) { - assert(s != NULL); - assert(r != NULL); - char *endptr = NULL; - int ret = strtol(s, &endptr, 10); - if (endptr == s) - return false; - *r = ret; - return true; -} - -// Fill the proc path of a thread given its id. -void FillProcPath(int pid, char *path, int path_size) { - char pid_str[32]; - snprintf(pid_str, sizeof(pid_str), "%d", pid); - snprintf(path, path_size, "/proc/%s/", pid_str); -} - -// Read thread info from /proc/$pid/status. -bool ReadThreadInfo(int pid, ThreadInfo *info) { - assert(info != NULL); - char status_path[80]; - // Max size we want to read from status file. - static const int kStatusMaxSize = 1024; - char status_content[kStatusMaxSize]; - - FillProcPath(pid, status_path, sizeof(status_path)); - strcat(status_path, "status"); - int fd = open(status_path, O_RDONLY, 0); - if (fd < 0) - return false; - - int num_read = read(fd, status_content, kStatusMaxSize - 1); - if (num_read < 0) { - close(fd); - return false; - } - close(fd); - status_content[num_read] = '\0'; - - char *tgid_start = strstr(status_content, "Tgid:"); - if (tgid_start) - sscanf(tgid_start, "Tgid:\t%d\n", &(info->tgid)); - else - // tgid not supported by kernel?? - info->tgid = 0; - - tgid_start = strstr(status_content, "Pid:"); - if (tgid_start) { - sscanf(tgid_start, "Pid:\t%d\n" "PPid:\t%d\n", &(info->pid), - &(info->ppid)); - return true; - } - return false; -} - -// Callback invoked for each mapped module. -// It use the module's adderss range to validate the address. -bool IsAddressInModuleCallback(const ModuleInfo &module_info, - void *context) { - AddressValidatingContext *addr = - reinterpret_cast(context); - addr->is_mapped = ((addr->address >= module_info.start_addr) && - (addr->address <= module_info.start_addr + - module_info.size)); - return !addr->is_mapped; -} - -#if defined(__i386__) && !defined(NO_FRAME_POINTER) -void *GetNextFrame(void **last_ebp) { - void *sp = *last_ebp; - if ((unsigned long)sp == (unsigned long)last_ebp) - return NULL; - if ((unsigned long)sp & (sizeof(void *) - 1)) - return NULL; - if ((unsigned long)sp - (unsigned long)last_ebp > 100000) - return NULL; - return sp; -} -#else -void *GetNextFrame(void **last_ebp) { - return reinterpret_cast(last_ebp); -} -#endif - -// Suspend a thread by attaching to it. -bool SuspendThread(int pid, void *context) { - // This may fail if the thread has just died or debugged. - errno = 0; - if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && - errno != 0) { - return false; - } - while (waitpid(pid, NULL, __WALL) < 0) { - if (errno != EINTR) { - ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } - } - return true; -} - -// Resume a thread by detaching from it. -bool ResumeThread(int pid, void *context) { - return ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; -} - -// Callback to get the thread information. -// Will be called for each thread found. -bool ThreadInfoCallback(int pid, void *context) { - CallbackParam *thread_callback = - reinterpret_cast *>(context); - ThreadInfo thread_info; - if (ReadThreadInfo(pid, &thread_info) && thread_callback) { - // Invoke callback from caller. - return (thread_callback->call_back)(thread_info, thread_callback->context); - } - return false; -} - -} // namespace - -namespace google_breakpad { - -LinuxThread::LinuxThread(int pid) : pid_(pid) , threads_suspened_(false) { -} - -LinuxThread::~LinuxThread() { - if (threads_suspened_) - ResumeAllThreads(); -} - -int LinuxThread::SuspendAllThreads() { - CallbackParam callback_param(SuspendThread, NULL); - int thread_count = 0; - if ((thread_count = IterateProcSelfTask(pid_, &callback_param)) > 0) - threads_suspened_ = true; - return thread_count; -} - -void LinuxThread::ResumeAllThreads() const { - CallbackParam callback_param(ResumeThread, NULL); - IterateProcSelfTask(pid_, &callback_param); -} - -int LinuxThread::GetThreadCount() const { - return IterateProcSelfTask(pid_, NULL); -} - -int LinuxThread::ListThreads( - CallbackParam *thread_callback_param) const { - CallbackParam callback_param(ThreadInfoCallback, - thread_callback_param); - return IterateProcSelfTask(pid_, &callback_param); -} - -bool LinuxThread::GetRegisters(int pid, user_regs_struct *regs) const { - assert(regs); - return (regs != NULL && - (ptrace(PTRACE_GETREGS, pid, NULL, regs) == 0) && - errno == 0); -} - -// Get the floating-point registers of a thread. -// The caller must get the thread pid by ListThreads. -bool LinuxThread::GetFPRegisters(int pid, user_fpregs_struct *regs) const { - assert(regs); - return (regs != NULL && - (ptrace(PTRACE_GETREGS, pid, NULL, regs) ==0) && - errno == 0); -} - -bool LinuxThread::GetFPXRegisters(int pid, user_fpxregs_struct *regs) const { - assert(regs); - return (regs != NULL && - (ptrace(PTRACE_GETFPREGS, pid, NULL, regs) != 0) && - errno == 0); -} - -bool LinuxThread::GetDebugRegisters(int pid, DebugRegs *regs) const { - assert(regs); - -#define GET_DR(name, num)\ - name->dr##num = ptrace(PTRACE_PEEKUSER, pid,\ - offsetof(struct user, u_debugreg[num]), NULL) - GET_DR(regs, 0); - GET_DR(regs, 1); - GET_DR(regs, 2); - GET_DR(regs, 3); - GET_DR(regs, 4); - GET_DR(regs, 5); - GET_DR(regs, 6); - GET_DR(regs, 7); - return true; -} - -int LinuxThread::GetThreadStackDump(uintptr_t current_ebp, - uintptr_t current_esp, - void *buf, - int buf_size) const { - assert(buf); - assert(buf_size > 0); - - uintptr_t stack_bottom = GetThreadStackBottom(current_ebp); - int size = stack_bottom - current_esp; - size = buf_size > size ? size : buf_size; - if (size > 0) - memcpy(buf, reinterpret_cast(current_esp), size); - return size; -} - -// Get the stack bottom of a thread by stack walking. It works -// unless the stack has been corrupted or the frame pointer has been omited. -// This is just a temporary solution before we get better ideas about how -// this can be done. -// -// We will check each frame address by checking into module maps. -// TODO(liuli): Improve it. -uintptr_t LinuxThread::GetThreadStackBottom(uintptr_t current_ebp) const { - void **sp = reinterpret_cast(current_ebp); - void **previous_sp = sp; - while (sp && IsAddressMapped((uintptr_t)sp)) { - previous_sp = sp; - sp = reinterpret_cast(GetNextFrame(sp)); - } - return (uintptr_t)previous_sp; -} - -int LinuxThread::GetModuleCount() const { - return ListModules(NULL); -} - -int LinuxThread::ListModules( - CallbackParam *callback_param) const { - char line[512]; - const char *maps_path = "/proc/self/maps"; - - int module_count = 0; - FILE *fp = fopen(maps_path, "r"); - if (fp == NULL) - return -1; - - uintptr_t start_addr; - uintptr_t end_addr; - while (fgets(line, sizeof(line), fp) != NULL) { - if (sscanf(line, "%x-%x", &start_addr, &end_addr) == 2) { - ModuleInfo module; - memset(&module, 0, sizeof(module)); - module.start_addr = start_addr; - module.size = end_addr - start_addr; - char *name = NULL; - assert(module.size > 0); - // Only copy name if the name is a valid path name. - if ((name = strchr(line, '/')) != NULL) { - // Get rid of the last '\n' in line - char *last_return = strchr(line, '\n'); - if (last_return != NULL) - *last_return = '\0'; - // Keep a space for the ending 0. - strncpy(module.name, name, sizeof(module.name) - 1); - ++module_count; - } - if (callback_param && - !(callback_param->call_back(module, callback_param->context))) - break; - } - } - fclose(fp); - return module_count; -} - -// Parse /proc/$pid/tasks to list all the threads of the process identified by -// pid. -int LinuxThread::IterateProcSelfTask(int pid, - CallbackParam *callback_param) const { - char task_path[80]; - FillProcPath(pid, task_path, sizeof(task_path)); - strcat(task_path, "task"); - - DIR *dir = opendir(task_path); - if (dir == NULL) - return -1; - - int pid_number = 0; - // Record the last pid we've found. This is used for duplicated thread - // removal. Duplicated thread information can be found in /proc/$pid/tasks. - int last_pid = -1; - struct dirent *entry = NULL; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") && - strcmp(entry->d_name, "..")) { - int tpid = 0; - if (LocalAtoi(entry->d_name, &tpid) && - last_pid != tpid) { - last_pid = tpid; - ++pid_number; - // Invoke the callback. - if (callback_param && - !(callback_param->call_back)(tpid, callback_param->context)) - break; - } - } - } - closedir(dir); - return pid_number; -} - -// Check if the address is a valid virtual address. -// If the address is in any of the mapped modules, we take it as valid. -// Otherwise it is invalid. -bool LinuxThread::IsAddressMapped(uintptr_t address) const { - AddressValidatingContext addr; - addr.address = address; - CallbackParam callback_param(IsAddressInModuleCallback, - &addr); - ListModules(&callback_param); - return addr.is_mapped; -} - -bool LinuxThread::FindSigContext(uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) { - uintptr_t previous_ebp; - const int MAX_STACK_DEPTH = 10; - int depth_counter = 0; - - do { - // We're looking for a |struct sigcontext| as the second parameter - // to a signal handler function call. Luckily, the sigcontext - // has an ebp member which should match the ebp pointed to - // by the ebp of the signal handler frame. - previous_ebp = reinterpret_cast(GetNextFrame( - reinterpret_cast(sighandler_ebp))); - // The stack looks like this: - // | previous ebp | previous eip | first param | second param |, - // so we need to offset by 3 to get to the second parameter. - *sig_ctx = reinterpret_cast(sighandler_ebp + - 3 * sizeof(uintptr_t)); - sighandler_ebp = previous_ebp; - depth_counter++; - } while(previous_ebp != (*sig_ctx)->ebp && sighandler_ebp != 0 && - IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH); - - return previous_ebp == (*sig_ctx)->ebp && previous_ebp != 0; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h deleted file mode 100644 index f738c2e0ba58..000000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// 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. -// -#ifndef CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ -#define CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ - -#include -#include - -namespace google_breakpad { - -// Max module path name length. -#define kMaxModuleNameLength 256 - -// Holding information about a thread in the process. -struct ThreadInfo { - // Id of the thread group. - int tgid; - // Id of the thread. - int pid; - // Id of the parent process. - int ppid; -}; - -// Holding infomaton about a module in the process. -struct ModuleInfo { - char name[kMaxModuleNameLength]; - uintptr_t start_addr; - int size; -}; - -// Holding debug registers. -struct DebugRegs { - int dr0; - int dr1; - int dr2; - int dr3; - int dr4; - int dr5; - int dr6; - int dr7; -}; - -// A callback to run when got a thread in the process. -// Return true will go on to the next thread while return false will stop the -// iteration. -typedef bool (*ThreadCallback)(const ThreadInfo &thread_info, void *context); - -// A callback to run when a new module is found in the process. -// Return true will go on to the next module while return false will stop the -// iteration. -typedef bool (*ModuleCallback)(const ModuleInfo &module_info, void *context); - -// Holding the callback information. -template -struct CallbackParam { - // Callback function address. - CallbackFunc call_back; - // Callback context; - void *context; - - CallbackParam() : call_back(NULL), context(NULL) { - } - - CallbackParam(CallbackFunc func, void *func_context) : - call_back(func), context(func_context) { - } -}; - -/////////////////////////////////////////////////////////////////////////////// - -// -// LinuxThread -// -// Provides handy support for operation on linux threads. -// It uses ptrace to get thread registers. Since ptrace only works in a -// different process other than the one being ptraced, user of this class -// should create another process before using the class. -// -// The process should be created in the following way: -// int cloned_pid = clone(ProcessEntryFunction, stack_address, -// CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, -// (void*)&arguments); -// waitpid(cloned_pid, NULL, __WALL); -// -// If CLONE_VM is not used, GetThreadStackBottom, GetThreadStackDump -// will not work since it just use memcpy to get the stack dump. -// -class LinuxThread { - public: - // Create a LinuxThread instance to list all the threads in a process. - explicit LinuxThread(int pid); - ~LinuxThread(); - - // Stop all the threads in the process. - // Return the number of stopped threads in the process. - // Return -1 means failed to stop threads. - int SuspendAllThreads(); - - // Resume all the suspended threads. - void ResumeAllThreads() const; - - // Get the count of threads in the process. - // Return -1 means error. - int GetThreadCount() const; - - // List the threads of process. - // Whenever there is a thread found, the callback will be invoked to process - // the information. - // Return number of threads listed. - int ListThreads(CallbackParam *thread_callback_param) const; - - // Get the general purpose registers of a thread. - // The caller must get the thread pid by ListThreads. - bool GetRegisters(int pid, user_regs_struct *regs) const; - - // Get the floating-point registers of a thread. - // The caller must get the thread pid by ListThreads. - bool GetFPRegisters(int pid, user_fpregs_struct *regs) const; - - // Get all the extended floating-point registers. May not work on all - // machines. - // The caller must get the thread pid by ListThreads. - bool GetFPXRegisters(int pid, user_fpxregs_struct *regs) const; - - // Get the debug registers. - // The caller must get the thread pid by ListThreads. - bool GetDebugRegisters(int pid, DebugRegs *regs) const; - - // Get the stack memory dump. - int GetThreadStackDump(uintptr_t current_ebp, - uintptr_t current_esp, - void *buf, - int buf_size) const; - - // Get the module count of the current process. - int GetModuleCount() const; - - // Get the mapped modules in the address space. - // Whenever a module is found, the callback will be invoked to process the - // information. - // Return how may modules are found. - int ListModules(CallbackParam *callback_param) const; - - // Get the bottom of the stack from ebp. - uintptr_t GetThreadStackBottom(uintptr_t current_ebp) const; - - // Finds a sigcontext on the stack given the ebp of our signal handler. - bool FindSigContext(uintptr_t sighandler_ebp, struct sigcontext **sig_ctx); - - private: - // This callback will run when a new thread has been found. - typedef bool (*PidCallback)(int pid, void *context); - - // Read thread information from /proc/$pid/task. - // Whenever a thread has been found, and callback will be invoked with - // the pid of the thread. - // Return number of threads found. - // Return -1 means the directory doesn't exist. - int IterateProcSelfTask(int pid, - CallbackParam *callback_param) const; - - // Check if the address is a valid virtual address. - bool IsAddressMapped(uintptr_t address) const; - - private: - // The pid of the process we are listing threads. - int pid_; - - // Mark if we have suspended the threads. - bool threads_suspened_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc deleted file mode 100644 index aeb5e64c90b8..000000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// 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 -#include -#include -#include - -#include -#include -#include - -#include "client/linux/handler/linux_thread.h" - -using namespace google_breakpad; - -// Thread use this to see if it should stop working. -static bool should_exit = false; - -static void foo2(int *a) { - // Stack variable, used for debugging stack dumps. - int c = 0xcccccccc; - c = c; - while (!should_exit) - sleep(1); -} - -static void foo() { - // Stack variable, used for debugging stack dumps. - int a = 0xaaaaaaaa; - foo2(&a); -} - -static void *thread_main(void *) { - // Stack variable, used for debugging stack dumps. - int b = 0xbbbbbbbb; - b = b; - while (!should_exit) { - foo(); - } - return NULL; -} - -static void CreateThreads(int num) { - pthread_t handle; - for (int i = 0; i < num; i++) { - if (0 != pthread_create(&handle, NULL, thread_main, NULL)) - fprintf(stderr, "Failed to create thread.\n"); - else - pthread_detach(handle); - } -} - -static bool ProcessOneModule(const struct ModuleInfo &module_info, - void *context) { - printf("0x%x[%8d] %s\n", module_info.start_addr, module_info.size, - module_info.name); - return true; -} - -static bool ProcessOneThread(const struct ThreadInfo &thread_info, - void *context) { - printf("\n\nPID: %d, TGID: %d, PPID: %d\n", - thread_info.pid, - thread_info.tgid, - thread_info.ppid); - - struct user_regs_struct regs; - struct user_fpregs_struct fp_regs; - struct user_fpxregs_struct fpx_regs; - struct DebugRegs dbg_regs; - - LinuxThread *threads = reinterpret_cast(context); - memset(®s, 0, sizeof(regs)); - if (threads->GetRegisters(thread_info.pid, ®s)) { - printf(" gs = 0x%lx\n", regs.xgs); - printf(" fs = 0x%lx\n", regs.xfs); - printf(" es = 0x%lx\n", regs.xes); - printf(" ds = 0x%lx\n", regs.xds); - printf(" edi = 0x%lx\n", regs.edi); - printf(" esi = 0x%lx\n", regs.esi); - printf(" ebx = 0x%lx\n", regs.ebx); - printf(" edx = 0x%lx\n", regs.edx); - printf(" ecx = 0x%lx\n", regs.ecx); - printf(" eax = 0x%lx\n", regs.eax); - printf(" ebp = 0x%lx\n", regs.ebp); - printf(" eip = 0x%lx\n", regs.eip); - printf(" cs = 0x%lx\n", regs.xcs); - printf(" eflags = 0x%lx\n", regs.eflags); - printf(" esp = 0x%lx\n", regs.esp); - printf(" ss = 0x%lx\n", regs.xss); - } else { - fprintf(stderr, "ERROR: Failed to get general purpose registers\n"); - } - memset(&fp_regs, 0, sizeof(fp_regs)); - if (threads->GetFPRegisters(thread_info.pid, &fp_regs)) { - printf("\n Floating point registers:\n"); - printf(" fctl = 0x%lx\n", fp_regs.cwd); - printf(" fstat = 0x%lx\n", fp_regs.swd); - printf(" ftag = 0x%lx\n", fp_regs.twd); - printf(" fioff = 0x%lx\n", fp_regs.fip); - printf(" fiseg = 0x%lx\n", fp_regs.fcs); - printf(" fooff = 0x%lx\n", fp_regs.foo); - printf(" foseg = 0x%lx\n", fp_regs.fos); - int st_space_size = sizeof(fp_regs.st_space) / sizeof(fp_regs.st_space[0]); - printf(" st_space[%2d] = 0x", st_space_size); - for (int i = 0; i < st_space_size; ++i) - printf("%02lx", fp_regs.st_space[i]); - printf("\n"); - } else { - fprintf(stderr, "ERROR: Failed to get floating-point registers\n"); - } - memset(&fpx_regs, 0, sizeof(fpx_regs)); - if (threads->GetFPXRegisters(thread_info.pid, &fpx_regs)) { - printf("\n Extended floating point registers:\n"); - printf(" fctl = 0x%x\n", fpx_regs.cwd); - printf(" fstat = 0x%x\n", fpx_regs.swd); - printf(" ftag = 0x%x\n", fpx_regs.twd); - printf(" fioff = 0x%lx\n", fpx_regs.fip); - printf(" fiseg = 0x%lx\n", fpx_regs.fcs); - printf(" fooff = 0x%lx\n", fpx_regs.foo); - printf(" foseg = 0x%lx\n", fpx_regs.fos); - printf(" fop = 0x%x\n", fpx_regs.fop); - printf(" mxcsr = 0x%lx\n", fpx_regs.mxcsr); - int space_size = sizeof(fpx_regs.st_space) / sizeof(fpx_regs.st_space[0]); - printf(" st_space[%2d] = 0x", space_size); - for (int i = 0; i < space_size; ++i) - printf("%02lx", fpx_regs.st_space[i]); - printf("\n"); - space_size = sizeof(fpx_regs.xmm_space) / sizeof(fpx_regs.xmm_space[0]); - printf(" xmm_space[%2d] = 0x", space_size); - for (int i = 0; i < space_size; ++i) - printf("%02lx", fpx_regs.xmm_space[i]); - printf("\n"); - } - if (threads->GetDebugRegisters(thread_info.pid, &dbg_regs)) { - printf("\n Debug registers:\n"); - printf(" dr0 = 0x%x\n", dbg_regs.dr0); - printf(" dr1 = 0x%x\n", dbg_regs.dr1); - printf(" dr2 = 0x%x\n", dbg_regs.dr2); - printf(" dr3 = 0x%x\n", dbg_regs.dr3); - printf(" dr4 = 0x%x\n", dbg_regs.dr4); - printf(" dr5 = 0x%x\n", dbg_regs.dr5); - printf(" dr6 = 0x%x\n", dbg_regs.dr6); - printf(" dr7 = 0x%x\n", dbg_regs.dr7); - printf("\n"); - } - if (regs.esp != 0) { - // Print the stack content. - int size = 1024 * 2; - char *buf = new char[size]; - size = threads->GetThreadStackDump(regs.ebp, - regs.esp, - (void*)buf, size); - printf(" Stack content: = 0x"); - size /= sizeof(unsigned long); - unsigned long *p_buf = (unsigned long *)(buf); - for (int i = 0; i < size; i += 1) - printf("%.8lx ", p_buf[i]); - delete []buf; - printf("\n"); - } - return true; -} - -static int PrintAllThreads(void *argument) { - int pid = (int)argument; - - LinuxThread threads(pid); - int total_thread = threads.SuspendAllThreads(); - printf("There are %d threads in the process: %d\n", total_thread, pid); - int total_module = threads.GetModuleCount(); - printf("There are %d modules in the process: %d\n", total_module, pid); - CallbackParam module_callback(ProcessOneModule, &threads); - threads.ListModules(&module_callback); - CallbackParam thread_callback(ProcessOneThread, &threads); - threads.ListThreads(&thread_callback); - return 0; -} - -int main(int argc, char **argv) { - int pid = getpid(); - printf("Main thread is %d\n", pid); - CreateThreads(1); - // Create stack for the process. - char *stack = new char[1024 * 100]; - int cloned_pid = clone(PrintAllThreads, stack + 1024 * 100, - CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, - (void*)getpid()); - waitpid(cloned_pid, NULL, __WALL); - should_exit = true; - printf("Test finished.\n"); - - delete []stack; - return 0; -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc deleted file mode 100644 index d172092c89e8..000000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common/linux/file_id.h" -#include "client/linux/handler/linux_thread.h" -#include "client/minidump_file_writer.h" -#include "client/minidump_file_writer-inl.h" -#include "google_breakpad/common/minidump_format.h" -#include "client/linux/handler/minidump_generator.h" - -#ifndef CLONE_UNTRACED -#define CLONE_UNTRACED 0x00800000 -#endif - -// This unnamed namespace contains helper functions. -namespace { - -using namespace google_breakpad; - -// Argument for the writer function. -struct WriterArgument { - MinidumpFileWriter *minidump_writer; - - // Context for the callback. - void *version_context; - - // Pid of the thread who called WriteMinidumpToFile - int requester_pid; - - // The stack bottom of the thread which caused the dump. - // Mainly used to find the thread id of the crashed thread since signal - // handler may not be called in the thread who caused it. - uintptr_t crashed_stack_bottom; - - // Pid of the crashing thread. - int crashed_pid; - - // Signal number when crash happed. Can be 0 if this is a requested dump. - int signo; - - // The ebp of the signal handler frame. Can be zero if this - // is a requested dump. - uintptr_t sighandler_ebp; - - // Signal context when crash happed. Can be NULL if this is a requested dump. - // This is actually an out parameter, but it will be filled in at the start - // of the writer thread. - struct sigcontext *sig_ctx; - - // Used to get information about the threads. - LinuxThread *thread_lister; -}; - -// Holding context information for the callback of finding the crashing thread. -struct FindCrashThreadContext { - const LinuxThread *thread_lister; - uintptr_t crashing_stack_bottom; - int crashing_thread_pid; - - FindCrashThreadContext() : - thread_lister(NULL), - crashing_stack_bottom(0UL), - crashing_thread_pid(-1) { - } -}; - -// Callback for list threads. -// It will compare the stack bottom of the provided thread with the stack -// bottom of the crashed thread, it they are eqaul, this is thread is the one -// who crashed. -bool IsThreadCrashedCallback(const ThreadInfo &thread_info, void *context) { - FindCrashThreadContext *crashing_context = - static_cast(context); - const LinuxThread *thread_lister = crashing_context->thread_lister; - struct user_regs_struct regs; - if (thread_lister->GetRegisters(thread_info.pid, ®s)) { - uintptr_t last_ebp = regs.ebp; - uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp); - if (stack_bottom > last_ebp && - stack_bottom == crashing_context->crashing_stack_bottom) { - // Got it. Stop iteration. - crashing_context->crashing_thread_pid = thread_info.pid; - return false; - } - } - return true; -} - -// Find the crashing thread id. -// This is done based on stack bottom comparing. -int FindCrashingThread(uintptr_t crashing_stack_bottom, - int requester_pid, - const LinuxThread *thread_lister) { - FindCrashThreadContext context; - context.thread_lister = thread_lister; - context.crashing_stack_bottom = crashing_stack_bottom; - CallbackParam callback_param(IsThreadCrashedCallback, - &context); - thread_lister->ListThreads(&callback_param); - return context.crashing_thread_pid; -} - -// Write the thread stack info minidump. -bool WriteThreadStack(uintptr_t last_ebp, - uintptr_t last_esp, - const LinuxThread *thread_lister, - UntypedMDRVA *memory, - MDMemoryDescriptor *loc) { - // Maximum stack size for a thread. - uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp); - if (stack_bottom > last_esp) { - int size = stack_bottom - last_esp; - if (size > 0) { - if (!memory->Allocate(size)) - return false; - memory->Copy(reinterpret_cast(last_esp), size); - loc->start_of_memory_range = 0 | last_esp; - loc->memory = memory->location(); - } - return true; - } - return false; -} - -// Write CPU context based on signal context. -bool WriteContext(MDRawContextX86 *context, const struct sigcontext *sig_ctx, - const DebugRegs *debug_regs) { - assert(sig_ctx != NULL); - context->context_flags = MD_CONTEXT_X86_FULL; - context->gs = sig_ctx->gs; - context->fs = sig_ctx->fs; - context->es = sig_ctx->es; - context->ds = sig_ctx->ds; - context->cs = sig_ctx->cs; - context->ss = sig_ctx->ss; - context->edi = sig_ctx->edi; - context->esi = sig_ctx->esi; - context->ebp = sig_ctx->ebp; - context->esp = sig_ctx->esp; - context->ebx = sig_ctx->ebx; - context->edx = sig_ctx->edx; - context->ecx = sig_ctx->ecx; - context->eax = sig_ctx->eax; - context->eip = sig_ctx->eip; - context->eflags = sig_ctx->eflags; - if (sig_ctx->fpstate != NULL) { - context->context_flags = MD_CONTEXT_X86_FULL | - MD_CONTEXT_X86_FLOATING_POINT; - context->float_save.control_word = sig_ctx->fpstate->cw; - context->float_save.status_word = sig_ctx->fpstate->sw; - context->float_save.tag_word = sig_ctx->fpstate->tag; - context->float_save.error_offset = sig_ctx->fpstate->ipoff; - context->float_save.error_selector = sig_ctx->fpstate->cssel; - context->float_save.data_offset = sig_ctx->fpstate->dataoff; - context->float_save.data_selector = sig_ctx->fpstate->datasel; - memcpy(context->float_save.register_area, sig_ctx->fpstate->_st, - sizeof(context->float_save.register_area)); - } - - if (debug_regs != NULL) { - context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS; - context->dr0 = debug_regs->dr0; - context->dr1 = debug_regs->dr1; - context->dr2 = debug_regs->dr2; - context->dr3 = debug_regs->dr3; - context->dr6 = debug_regs->dr6; - context->dr7 = debug_regs->dr7; - } - return true; -} - -// Write CPU context based on provided registers. -bool WriteContext(MDRawContextX86 *context, - const struct user_regs_struct *regs, - const struct user_fpregs_struct *fp_regs, - const DebugRegs *dbg_regs) { - if (!context || !regs) - return false; - - context->context_flags = MD_CONTEXT_X86_FULL; - - context->cs = regs->xcs; - context->ds = regs->xds; - context->es = regs->xes; - context->fs = regs->xfs; - context->gs = regs->xgs; - context->ss = regs->xss; - context->edi = regs->edi; - context->esi = regs->esi; - context->ebx = regs->ebx; - context->edx = regs->edx; - context->ecx = regs->ecx; - context->eax = regs->eax; - context->ebp = regs->ebp; - context->eip = regs->eip; - context->esp = regs->esp; - context->eflags = regs->eflags; - - if (dbg_regs != NULL) { - context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS; - context->dr0 = dbg_regs->dr0; - context->dr1 = dbg_regs->dr1; - context->dr2 = dbg_regs->dr2; - context->dr3 = dbg_regs->dr3; - context->dr6 = dbg_regs->dr6; - context->dr7 = dbg_regs->dr7; - } - - if (fp_regs != NULL) { - context->context_flags |= MD_CONTEXT_X86_FLOATING_POINT; - context->float_save.control_word = fp_regs->cwd; - context->float_save.status_word = fp_regs->swd; - context->float_save.tag_word = fp_regs->twd; - context->float_save.error_offset = fp_regs->fip; - context->float_save.error_selector = fp_regs->fcs; - context->float_save.data_offset = fp_regs->foo; - context->float_save.data_selector = fp_regs->fos; - context->float_save.data_selector = fp_regs->fos; - - memcpy(context->float_save.register_area, fp_regs->st_space, - sizeof(context->float_save.register_area)); - } - return true; -} - -// Write information about a crashed thread. -// When a thread crash, kernel will write something on the stack for processing -// signal. This makes the current stack not reliable, and our stack walker -// won't figure out the whole call stack for this. So we write the stack at the -// time of the crash into the minidump file, not the current stack. -bool WriteCrashedThreadStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - const ThreadInfo &thread_info, - MDRawThread *thread) { - assert(writer_args->sig_ctx != NULL); - - thread->thread_id = thread_info.pid; - - UntypedMDRVA memory(minidump_writer); - if (!WriteThreadStack(writer_args->sig_ctx->ebp, - writer_args->sig_ctx->esp, - writer_args->thread_lister, - &memory, - &thread->stack)) - return false; - - TypedMDRVA context(minidump_writer); - if (!context.Allocate()) - return false; - thread->thread_context = context.location(); - memset(context.get(), 0, sizeof(MDRawContextX86)); - return WriteContext(context.get(), writer_args->sig_ctx, NULL); -} - -// Write information about a thread. -// This function only processes thread running normally at the crash. -bool WriteThreadStream(MinidumpFileWriter *minidump_writer, - const LinuxThread *thread_lister, - const ThreadInfo &thread_info, - MDRawThread *thread) { - thread->thread_id = thread_info.pid; - - struct user_regs_struct regs; - memset(®s, 0, sizeof(regs)); - if (!thread_lister->GetRegisters(thread_info.pid, ®s)) { - perror(NULL); - return false; - } - - UntypedMDRVA memory(minidump_writer); - if (!WriteThreadStack(regs.ebp, - regs.esp, - thread_lister, - &memory, - &thread->stack)) - return false; - - struct user_fpregs_struct fp_regs; - DebugRegs dbg_regs; - memset(&fp_regs, 0, sizeof(fp_regs)); - // Get all the registers. - thread_lister->GetFPRegisters(thread_info.pid, &fp_regs); - thread_lister->GetDebugRegisters(thread_info.pid, &dbg_regs); - - // Write context - TypedMDRVA context(minidump_writer); - if (!context.Allocate()) - return false; - thread->thread_context = context.location(); - memset(context.get(), 0, sizeof(MDRawContextX86)); - return WriteContext(context.get(), ®s, &fp_regs, &dbg_regs); -} - -bool WriteCPUInformation(MDRawSystemInfo *sys_info) { - const char *proc_cpu_path = "/proc/cpuinfo"; - char line[128]; - char vendor_id[13]; - const char vendor_id_name[] = "vendor_id"; - const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; - - struct CpuInfoEntry { - const char *info_name; - int value; - } cpu_info_table[] = { - { "processor", -1 }, - { "model", 0 }, - { "stepping", 0 }, - { "cpuid level", 0 }, - { NULL, -1 }, - }; - - memset(vendor_id, 0, sizeof(vendor_id)); - - FILE *fp = fopen(proc_cpu_path, "r"); - if (fp != NULL) { - while (fgets(line, sizeof(line), fp)) { - CpuInfoEntry *entry = &cpu_info_table[0]; - while (entry->info_name != NULL) { - if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { - char *value = strchr(line, ':'); - value++; - if (value != NULL) - sscanf(value, " %d", &(entry->value)); - } - entry++; - } - - // special case for vendor_id - if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { - char *value = strchr(line, ':'); - if (value == NULL) - continue; - - value++; - while (*value && isspace(*value)) - value++; - if (*value) { - size_t length = strlen(value); - // we don't want the trailing newline - if (value[length - 1] == '\n') - length--; - // ensure we have space for the value - if (length < sizeof(vendor_id)) - strncpy(vendor_id, value, length); - } - } - } - fclose(fp); - } - - // /proc/cpuinfo contains cpu id, change it into number by adding one. - cpu_info_table[0].value++; - - sys_info->number_of_processors = cpu_info_table[0].value; - sys_info->processor_level = cpu_info_table[3].value; - sys_info->processor_revision = cpu_info_table[1].value << 8 | - cpu_info_table[2].value; - - sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; - struct utsname uts; - if (uname(&uts) == 0) { - // Match i*86 and x86* as X86 architecture. - if ((strstr(uts.machine, "x86") == uts.machine) || - (strlen(uts.machine) == 4 && - uts.machine[0] == 'i' && - uts.machine[2] == '8' && - uts.machine[3] == '6')) { - sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86; - if (vendor_id[0] != '\0') - memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, - sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); - } - } - return true; -} - -bool WriteOSInformation(MinidumpFileWriter *minidump_writer, - MDRawSystemInfo *sys_info) { - sys_info->platform_id = MD_OS_LINUX; - - struct utsname uts; - if (uname(&uts) == 0) { - char os_version[512]; - size_t space_left = sizeof(os_version); - memset(os_version, 0, space_left); - const char *os_info_table[] = { - uts.sysname, - uts.release, - uts.version, - uts.machine, - "GNU/Linux", - NULL - }; - for (const char **cur_os_info = os_info_table; - *cur_os_info != NULL; - cur_os_info++) { - if (cur_os_info != os_info_table && space_left > 1) { - strcat(os_version, " "); - space_left--; - } - if (space_left > strlen(*cur_os_info)) { - strcat(os_version, *cur_os_info); - space_left -= strlen(*cur_os_info); - } else { - break; - } - } - - MDLocationDescriptor location; - if (!minidump_writer->WriteString(os_version, 0, &location)) - return false; - sys_info->csd_version_rva = location.rva; - } - return true; -} - -// Callback context for get writting thread information. -struct ThreadInfoCallbackCtx { - MinidumpFileWriter *minidump_writer; - const WriterArgument *writer_args; - TypedMDRVA *list; - int thread_index; -}; - -// Callback run for writing threads information in the process. -bool ThreadInfomationCallback(const ThreadInfo &thread_info, - void *context) { - ThreadInfoCallbackCtx *callback_context = - static_cast(context); - bool success = true; - MDRawThread thread; - memset(&thread, 0, sizeof(MDRawThread)); - if (thread_info.pid != callback_context->writer_args->crashed_pid || - callback_context->writer_args->sig_ctx == NULL) { - success = WriteThreadStream(callback_context->minidump_writer, - callback_context->writer_args->thread_lister, - thread_info, &thread); - } else { - success = WriteCrashedThreadStream(callback_context->minidump_writer, - callback_context->writer_args, - thread_info, &thread); - } - if (success) { - callback_context->list->CopyIndexAfterObject( - callback_context->thread_index++, - &thread, sizeof(MDRawThread)); - } - return success; -} - -// Stream writers -bool WriteThreadListStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - // Get the thread information. - const LinuxThread *thread_lister = writer_args->thread_lister; - int thread_count = thread_lister->GetThreadCount(); - if (thread_count < 0) - return false; - TypedMDRVA list(minidump_writer); - if (!list.AllocateObjectAndArray(thread_count, sizeof(MDRawThread))) - return false; - dir->stream_type = MD_THREAD_LIST_STREAM; - dir->location = list.location(); - list.get()->number_of_threads = thread_count; - - ThreadInfoCallbackCtx context; - context.minidump_writer = minidump_writer; - context.writer_args = writer_args; - context.list = &list; - context.thread_index = 0; - CallbackParam callback_param(ThreadInfomationCallback, - &context); - int written = thread_lister->ListThreads(&callback_param); - return written == thread_count; -} - -bool WriteCVRecord(MinidumpFileWriter *minidump_writer, - MDRawModule *module, - const char *module_path) { - TypedMDRVA cv(minidump_writer); - - // Only return the last path component of the full module path - const char *module_name = strrchr(module_path, '/'); - // Increment past the slash - if (module_name) - ++module_name; - else - module_name = ""; - - size_t module_name_length = strlen(module_name); - if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) - return false; - if (!cv.CopyIndexAfterObject(0, const_cast(module_name), - module_name_length)) - return false; - - module->cv_record = cv.location(); - MDCVInfoPDB70 *cv_ptr = cv.get(); - memset(cv_ptr, 0, sizeof(MDCVInfoPDB70)); - cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; - cv_ptr->age = 0; - - // Get the module identifier - FileID file_id(module_path); - unsigned char identifier[16]; - - if (file_id.ElfFileIdentifier(identifier)) { - cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | - (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | - (uint32_t)identifier[3]; - cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; - cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; - cv_ptr->signature.data4[0] = identifier[8]; - cv_ptr->signature.data4[1] = identifier[9]; - cv_ptr->signature.data4[2] = identifier[10]; - cv_ptr->signature.data4[3] = identifier[11]; - cv_ptr->signature.data4[4] = identifier[12]; - cv_ptr->signature.data4[5] = identifier[13]; - cv_ptr->signature.data4[6] = identifier[14]; - cv_ptr->signature.data4[7] = identifier[15]; - } - return true; -} - -struct ModuleInfoCallbackCtx { - MinidumpFileWriter *minidump_writer; - const WriterArgument *writer_args; - TypedMDRVA *list; - int module_index; -}; - -bool ModuleInfoCallback(const ModuleInfo &module_info, - void *context) { - ModuleInfoCallbackCtx *callback_context = - static_cast(context); - // Skip those modules without name, or those that are not modules. - if (strlen(module_info.name) == 0 || - !strchr(module_info.name, '/')) - return true; - - MDRawModule module; - memset(&module, 0, sizeof(module)); - MDLocationDescriptor loc; - if (!callback_context->minidump_writer->WriteString(module_info.name, 0, - &loc)) - return false; - module.base_of_image = (u_int64_t)module_info.start_addr; - module.size_of_image = module_info.size; - module.module_name_rva = loc.rva; - - if (!WriteCVRecord(callback_context->minidump_writer, &module, - module_info.name)) - return false; - callback_context->list->CopyIndexAfterObject( - callback_context->module_index++, &module, MD_MODULE_SIZE); - return true; -} - -bool WriteModuleListStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA list(minidump_writer); - int module_count = writer_args->thread_lister->GetModuleCount(); - if (module_count <= 0 || - !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE)) - return false; - dir->stream_type = MD_MODULE_LIST_STREAM; - dir->location = list.location(); - list.get()->number_of_modules = module_count; - ModuleInfoCallbackCtx context; - context.minidump_writer = minidump_writer; - context.writer_args = writer_args; - context.list = &list; - context.module_index = 0; - CallbackParam callback(ModuleInfoCallback, &context); - return writer_args->thread_lister->ListModules(&callback) == module_count; -} - -bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA sys_info(minidump_writer); - if (!sys_info.Allocate()) - return false; - dir->stream_type = MD_SYSTEM_INFO_STREAM; - dir->location = sys_info.location(); - - return WriteCPUInformation(sys_info.get()) && - WriteOSInformation(minidump_writer, sys_info.get()); -} - -bool WriteExceptionStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - // This happenes when this is not a crash, but a requested dump. - if (writer_args->sig_ctx == NULL) - return false; - - TypedMDRVA exception(minidump_writer); - if (!exception.Allocate()) - return false; - - dir->stream_type = MD_EXCEPTION_STREAM; - dir->location = exception.location(); - exception.get()->thread_id = writer_args->crashed_pid; - exception.get()->exception_record.exception_code = writer_args->signo; - exception.get()->exception_record.exception_flags = 0; - if (writer_args->sig_ctx != NULL) { - exception.get()->exception_record.exception_address = - writer_args->sig_ctx->eip; - } else { - return true; - } - - // Write context of the exception. - TypedMDRVA context(minidump_writer); - if (!context.Allocate()) - return false; - exception.get()->thread_context = context.location(); - memset(context.get(), 0, sizeof(MDRawContextX86)); - return WriteContext(context.get(), writer_args->sig_ctx, NULL); -} - -bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA info(minidump_writer); - if (!info.Allocate()) - return false; - - dir->stream_type = MD_MISC_INFO_STREAM; - dir->location = info.location(); - info.get()->size_of_info = sizeof(MDRawMiscInfo); - info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID; - info.get()->process_id = writer_args->requester_pid; - - return true; -} - -bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA info(minidump_writer); - if (!info.Allocate()) - return false; - - dir->stream_type = MD_BREAKPAD_INFO_STREAM; - dir->location = info.location(); - - info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | - MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; - info.get()->dump_thread_id = getpid(); - info.get()->requesting_thread_id = writer_args->requester_pid; - return true; -} - -// Prototype of writer functions. -typedef bool (*WriteStringFN)(MinidumpFileWriter *, - const WriterArgument *, - MDRawDirectory *); - -// Function table to writer a full minidump. -WriteStringFN writers[] = { - WriteThreadListStream, - WriteModuleListStream, - WriteSystemInfoStream, - WriteExceptionStream, - WriteMiscInfoStream, - WriteBreakpadInfoStream, -}; - -// Will call each writer function in the writers table. -// It runs in a different process from the crashing process, but sharing -// the same address space. This enables it to use ptrace functions. -int Write(void *argument) { - WriterArgument *writer_args = - static_cast(argument); - - if (!writer_args->thread_lister->SuspendAllThreads()) - return -1; - - if (writer_args->sighandler_ebp != 0 && - writer_args->thread_lister->FindSigContext(writer_args->sighandler_ebp, - &writer_args->sig_ctx)) { - writer_args->crashed_stack_bottom = - writer_args->thread_lister->GetThreadStackBottom( - writer_args->sig_ctx->ebp); - int crashed_pid = FindCrashingThread(writer_args->crashed_stack_bottom, - writer_args->requester_pid, - writer_args->thread_lister); - if (crashed_pid > 0) - writer_args->crashed_pid = crashed_pid; - } - - - MinidumpFileWriter *minidump_writer = writer_args->minidump_writer; - TypedMDRVA header(minidump_writer); - TypedMDRVA dir(minidump_writer); - if (!header.Allocate()) - return 0; - - int writer_count = sizeof(writers) / sizeof(writers[0]); - // Need directory space for all writers. - if (!dir.AllocateArray(writer_count)) - return 0; - header.get()->signature = MD_HEADER_SIGNATURE; - header.get()->version = MD_HEADER_VERSION; - header.get()->time_date_stamp = time(NULL); - header.get()->stream_count = writer_count; - header.get()->stream_directory_rva = dir.position(); - - int dir_index = 0; - MDRawDirectory local_dir; - for (int i = 0; i < writer_count; ++i) { - if (writers[i](minidump_writer, writer_args, &local_dir)) - dir.CopyIndex(dir_index++, &local_dir); - } - - writer_args->thread_lister->ResumeAllThreads(); - return 0; -} - -} // namespace - -namespace google_breakpad { - -MinidumpGenerator::MinidumpGenerator() { - AllocateStack(); -} - -MinidumpGenerator::~MinidumpGenerator() { -} - -void MinidumpGenerator::AllocateStack() { - stack_.reset(new char[kStackSize]); -} - -bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname, - int signo, - uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) const { - assert(file_pathname != NULL); - assert(stack_ != NULL); - - if (stack_ == NULL || file_pathname == NULL) - return false; - - MinidumpFileWriter minidump_writer; - if (minidump_writer.Open(file_pathname)) { - WriterArgument argument; - memset(&argument, 0, sizeof(argument)); - LinuxThread thread_lister(getpid()); - argument.thread_lister = &thread_lister; - argument.minidump_writer = &minidump_writer; - argument.requester_pid = getpid(); - argument.crashed_pid = getpid(); - argument.signo = signo; - argument.sighandler_ebp = sighandler_ebp; - argument.sig_ctx = NULL; - - int cloned_pid = clone(Write, stack_.get() + kStackSize, - CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, - (void*)&argument); - waitpid(cloned_pid, NULL, __WALL); - if (sig_ctx != NULL) - *sig_ctx = argument.sig_ctx; - return true; - } - - return false; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h new file mode 100644 index 000000000000..4698b7e5b917 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h @@ -0,0 +1,105 @@ +// Copyright (c) 2009, 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. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ + +#include +#include +#include +#include +#include +#include + +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for enumerating a directory without using diropen/readdir or other +// functions which may allocate memory. +class DirectoryReader { + public: + DirectoryReader(int fd) + : fd_(fd), + buf_used_(0) { + } + + // Return the next entry from the directory + // name: (output) the NUL terminated entry name + // + // Returns true iff successful (false on EOF). + // + // After calling this, one must call |PopEntry| otherwise you'll get the same + // entry over and over. + bool GetNextEntry(const char** name) { + struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + if (buf_used_ == 0) { + // need to read more entries. + const int n = sys_getdents(fd_, dent, sizeof(buf_)); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + } + + if (buf_used_ == 0 && hit_eof_) + return false; + + assert(buf_used_ > 0); + + *name = dent->d_name; + return true; + } + + void PopEntry() { + if (!buf_used_) + return; + + const struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + buf_used_ -= dent->d_reclen; + memmove(buf_, buf_ + dent->d_reclen, buf_used_); + } + + private: + const int fd_; + bool hit_eof_; + unsigned buf_used_; + uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc new file mode 100644 index 000000000000..3034e619c487 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2009, 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 +#include + +#include +#include +#include + +#include "client/linux/minidump_writer/directory_reader.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test DirectoryReaderTest; +} + +TEST(DirectoryReaderTest, CompareResults) { + std::set dent_set; + + DIR *const dir = opendir("/proc/self"); + ASSERT_TRUE(dir != NULL); + + struct dirent* dent; + while ((dent = readdir(dir))) + dent_set.insert(dent->d_name); + + closedir(dir); + + const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); + ASSERT_GE(fd, 0); + + DirectoryReader dir_reader(fd); + unsigned seen = 0; + + const char* name; + while (dir_reader.GetNextEntry(&name)) { + ASSERT_TRUE(dent_set.find(name) != dent_set.end()); + seen++; + dir_reader.PopEntry(); + } + + ASSERT_TRUE(dent_set.find("status") != dent_set.end()); + ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); + ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); + + ASSERT_EQ(dent_set.size(), seen); + close(fd); +} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h new file mode 100644 index 000000000000..5c0a1154b374 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h @@ -0,0 +1,130 @@ +// Copyright (c) 2009, 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. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ + +#include +#include +#include + +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for reading a file, line by line, without using fopen/fgets or other +// functions which may allocate memory. +class LineReader { + public: + LineReader(int fd) + : fd_(fd), + hit_eof_(false), + buf_used_(0) { + } + + // The maximum length of a line. + static const size_t kMaxLineLen = 512; + + // Return the next line from the file. + // line: (output) a pointer to the start of the line. The line is NUL + // terminated. + // len: (output) the length of the line (not inc the NUL byte) + // + // Returns true iff successful (false on EOF). + // + // One must call |PopLine| after this function, otherwise you'll continue to + // get the same line over and over. + bool GetNextLine(const char **line, unsigned *len) { + for (;;) { + if (buf_used_ == 0 && hit_eof_) + return false; + + for (unsigned i = 0; i < buf_used_; ++i) { + if (buf_[i] == '\n' || buf_[i] == 0) { + buf_[i] = 0; + *len = i; + *line = buf_; + return true; + } + } + + if (buf_used_ == sizeof(buf_)) { + // we scanned the whole buffer and didn't find an end-of-line marker. + // This line is too long to process. + return false; + } + + // We didn't find any end-of-line terminators in the buffer. However, if + // this is the last line in the file it might not have one: + if (hit_eof_) { + assert(buf_used_); + // There's room for the NUL because of the buf_used_ == sizeof(buf_) + // check above. + buf_[buf_used_] = 0; + *len = buf_used_; + buf_used_ += 1; // since we appended the NUL. + *line = buf_; + return true; + } + + // Otherwise, we should pull in more data from the file + const ssize_t n = sys_read(fd_, buf_ + buf_used_, + sizeof(buf_) - buf_used_); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + + // At this point, we have either set the hit_eof_ flag, or we have more + // data to process... + } + } + + void PopLine(unsigned len) { + // len doesn't include the NUL byte at the end. + + assert(buf_used_ >= len + 1); + buf_used_ -= len + 1; + memmove(buf_, buf_ + len + 1, buf_used_); + } + + private: + const int fd_; + + bool hit_eof_; + unsigned buf_used_; + char buf_[kMaxLineLen]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc new file mode 100644 index 000000000000..222a098eee15 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc @@ -0,0 +1,184 @@ +// Copyright (c) 2009, 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 +#include +#include + +#include "client/linux/minidump_writer/line_reader.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +static int TemporaryFile() { + static const char templ[] = "/tmp/line-reader-unittest-XXXXXX"; + char templ_copy[sizeof(templ)]; + memcpy(templ_copy, templ, sizeof(templ)); + const int fd = mkstemp(templ_copy); + if (fd >= 0) + unlink(templ_copy); + + return fd; +} + +namespace { +typedef testing::Test LineReaderTest; +} + +TEST(LineReaderTest, EmptyFile) { + const int fd = TemporaryFile(); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, OneLineTerminated) { + const int fd = TemporaryFile(); + write(fd, "a\n", 2); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, 1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, OneLine) { + const int fd = TemporaryFile(); + write(fd, "a", 1); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, 1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, TwoLinesTerminated) { + const int fd = TemporaryFile(); + write(fd, "a\nb\n", 4); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, 1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, 1); + ASSERT_EQ(line[0], 'b'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, TwoLines) { + const int fd = TemporaryFile(); + write(fd, "a\nb", 3); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, 1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, 1); + ASSERT_EQ(line[0], 'b'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, MaxLength) { + const int fd = TemporaryFile(); + char l[LineReader::kMaxLineLen - 1]; + memset(l, 'a', sizeof(l)); + write(fd, l, sizeof(l)); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, sizeof(l)); + ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); + ASSERT_EQ(line[len], 0); + + close(fd); +} + +TEST(LineReaderTest, TooLong) { + const int fd = TemporaryFile(); + char l[LineReader::kMaxLineLen]; + memset(l, 'a', sizeof(l)); + write(fd, l, sizeof(l)); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc new file mode 100644 index 000000000000..86ebb866eee2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc @@ -0,0 +1,421 @@ +// Copyright (c) 2009, 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. + +// This code deals with the mechanics of getting information about a crashed +// process. Since this code may run in a compromised address space, the same +// rules apply as detailed at the top of minidump_writer.h: no libc calls and +// use the alternative allocator. + +#include "client/linux/minidump_writer/linux_dumper.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "client/linux/minidump_writer/directory_reader.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" + +// Suspend a thread by attaching to it. +static bool SuspendThread(pid_t pid) { + // This may fail if the thread has just died or debugged. + errno = 0; + if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && + errno != 0) { + return false; + } + while (sys_waitpid(pid, NULL, __WALL) < 0) { + if (errno != EINTR) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } + } + return true; +} + +// Resume a thread by detaching from it. +static bool ResumeThread(pid_t pid) { + return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; +} + +namespace google_breakpad { + +LinuxDumper::LinuxDumper(int pid) + : pid_(pid), + threads_suspened_(false), + threads_(&allocator_, 8), + mappings_(&allocator_) { +} + +bool LinuxDumper::Init() { + return EnumerateThreads(&threads_) && + EnumerateMappings(&mappings_); +} + +bool LinuxDumper::ThreadsSuspend() { + if (threads_suspened_) + return true; + bool good = true; + for (size_t i = 0; i < threads_.size(); ++i) + good &= SuspendThread(threads_[i]); + threads_suspened_ = true; + return good; +} + +bool LinuxDumper::ThreadsResume() { + if (!threads_suspened_) + return false; + bool good = true; + for (size_t i = 0; i < threads_.size(); ++i) + good &= ResumeThread(threads_[i]); + threads_suspened_ = false; + return good; +} + +void +LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { + assert(path); + if (!path) { + return; + } + + path[0] = '\0'; + + const unsigned pid_len = my_int_len(pid); + + assert(node); + if (!node) { + return; + } + + size_t node_len = my_strlen(node); + assert(node_len < NAME_MAX); + if (node_len >= NAME_MAX) { + return; + } + + assert(node_len > 0); + if (node_len == 0) { + return; + } + + assert(pid > 0); + if (pid <= 0) { + return; + } + + const size_t total_length = 6 + pid_len + 1 + node_len; + + assert(total_length < NAME_MAX); + if (total_length >= NAME_MAX) { + return; + } + + memcpy(path, "/proc/", 6); + my_itos(path + 6, pid, pid_len); + memcpy(path + 6 + pid_len, "/", 1); + memcpy(path + 6 + pid_len + 1, node, node_len); + memcpy(path + total_length, "\0", 1); +} + +void* +LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { + char auxv_path[80]; + BuildProcPath(auxv_path, pid, "auxv"); + + // If BuildProcPath errors out due to invalid input, we'll handle it when + // we try to sys_open the file. + + // Find the AT_SYSINFO_EHDR entry for linux-gate.so + // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more + // information. + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + + elf_aux_entry one_aux_entry; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { + close(fd); + return reinterpret_cast(one_aux_entry.a_un.a_val); + } + } + close(fd); + return NULL; +} + +bool +LinuxDumper::EnumerateMappings(wasteful_vector* result) const { + char maps_path[80]; + BuildProcPath(maps_path, pid_, "maps"); + + // linux_gate_loc is the beginning of the kernel's mapping of + // linux-gate.so in the process. It doesn't actually show up in the + // maps list as a filename, so we use the aux vector to find it's + // load location and special case it's entry when creating the list + // of mappings. + const void* linux_gate_loc; + linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); + + const int fd = sys_open(maps_path, O_RDONLY, 0); + if (fd < 0) + return false; + LineReader* const line_reader = new(allocator_) LineReader(fd); + + const char* line; + unsigned line_len; + while (line_reader->GetNextLine(&line, &line_len)) { + uintptr_t start_addr, end_addr, offset; + + const char* i1 = my_read_hex_ptr(&start_addr, line); + if (*i1 == '-') { + const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); + if (*i2 == ' ') { + const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); + if (*i3 == ' ') { + MappingInfo* const module = new(allocator_) MappingInfo; + memset(module, 0, sizeof(MappingInfo)); + module->start_addr = start_addr; + module->size = end_addr - start_addr; + module->offset = offset; + const char* name = NULL; + // Only copy name if the name is a valid path name, or if + // we've found the VDSO image + if ((name = my_strchr(line, '/')) != NULL) { + const unsigned l = my_strlen(name); + if (l < sizeof(module->name)) + memcpy(module->name, name, l); + } else if (linux_gate_loc && + reinterpret_cast(module->start_addr) == + linux_gate_loc) { + memcpy(module->name, + kLinuxGateLibraryName, + my_strlen(kLinuxGateLibraryName)); + module->offset = 0; + } + result->push_back(module); + } + } + } + line_reader->PopLine(line_len); + } + + sys_close(fd); + + return result->size() > 0; +} + +// Parse /proc/$pid/task to list all the threads of the process identified by +// pid. +bool LinuxDumper::EnumerateThreads(wasteful_vector* result) const { + char task_path[80]; + BuildProcPath(task_path, pid_, "task"); + + const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); + if (fd < 0) + return false; + DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); + + // The directory may contain duplicate entries which we filter by assuming + // that they are consecutive. + int last_tid = -1; + const char* dent_name; + while (dir_reader->GetNextEntry(&dent_name)) { + if (my_strcmp(dent_name, ".") && + my_strcmp(dent_name, "..")) { + int tid = 0; + if (my_strtoui(&tid, dent_name) && + last_tid != tid) { + last_tid = tid; + result->push_back(tid); + } + } + dir_reader->PopEntry(); + } + + sys_close(fd); + return true; +} + +// Read thread info from /proc/$pid/status. +// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible, +// these members are set to -1. Returns true iff all three members are +// availible. +bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { + assert(info != NULL); + char status_path[80]; + BuildProcPath(status_path, tid, "status"); + + const int fd = open(status_path, O_RDONLY); + if (fd < 0) + return false; + + LineReader* const line_reader = new(allocator_) LineReader(fd); + const char* line; + unsigned line_len; + + info->ppid = info->tgid = -1; + + while (line_reader->GetNextLine(&line, &line_len)) { + if (my_strncmp("Tgid:\t", line, 6) == 0) { + my_strtoui(&info->tgid, line + 6); + } else if (my_strncmp("PPid:\t", line, 6) == 0) { + my_strtoui(&info->ppid, line + 6); + } + + line_reader->PopLine(line_len); + } + + if (info->ppid == -1 || info->tgid == -1) + return false; + + if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 || + sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { + return false; + } + +#if defined(__i386) + if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) + return false; +#endif + +#if defined(__i386) || defined(__x86_64) + for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { + if (sys_ptrace( + PTRACE_PEEKUSER, tid, + reinterpret_cast (offsetof(struct user, + u_debugreg[0]) + i * + sizeof(debugreg_t)), + &info->dregs[i]) == -1) { + return false; + } + } +#endif + + const uint8_t* stack_pointer; +#if defined(__i386) + memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#else +#error "This code hasn't been ported to your platform yet." +#endif + + if (!GetStackInfo(&info->stack, &info->stack_len, + (uintptr_t) stack_pointer)) + return false; + + return true; +} + +// Get information about the stack, given the stack pointer. We don't try to +// walk the stack since we might not have all the information needed to do +// unwind. So we just grab, up to, 32k of stack. +bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, + uintptr_t int_stack_pointer) { +#if defined(__i386) || defined(__x86_64) + static const bool stack_grows_down = true; + static const uintptr_t page_size = 4096; +#else +#error "This code has not been ported to your platform yet." +#endif + // Move the stack pointer to the bottom of the page that it's in. + uint8_t* const stack_pointer = + reinterpret_cast(int_stack_pointer & ~(page_size - 1)); + + // The number of bytes of stack which we try to capture. + static unsigned kStackToCapture = 32 * 1024; + + const MappingInfo* mapping = FindMapping(stack_pointer); + if (!mapping) + return false; + if (stack_grows_down) { + const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; + const ptrdiff_t distance_to_end = + static_cast(mapping->size) - offset; + *stack_len = distance_to_end > kStackToCapture ? + kStackToCapture : distance_to_end; + *stack = stack_pointer; + } else { + const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; + *stack_len = offset > kStackToCapture ? kStackToCapture : offset; + *stack = stack_pointer - *stack_len; + } + + return true; +} + +// static +void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length) { + unsigned long tmp; + size_t done = 0; + static const size_t word_size = sizeof(tmp); + uint8_t* const local = (uint8_t*) dest; + uint8_t* const remote = (uint8_t*) src; + + while (done < length) { + const size_t l = length - done > word_size ? word_size : length - done; + if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) + tmp = 0; + memcpy(local + done, &tmp, l); + done += l; + } +} + +// Find the mapping which the given memory address falls in. +const MappingInfo* LinuxDumper::FindMapping(const void* address) const { + const uintptr_t addr = (uintptr_t) address; + + for (size_t i = 0; i < mappings_.size(); ++i) { + const uintptr_t start = static_cast(mappings_[i]->start_addr); + if (addr >= start && addr - start < mappings_[i]->size) + return mappings_[i]; + } + + return NULL; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h new file mode 100644 index 000000000000..d8e5e783e2d5 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h @@ -0,0 +1,146 @@ +// Copyright (c) 2009, 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. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ + +#include +#include +#include +#include +#include + +#include "common/linux/memory.h" + +namespace google_breakpad { + +typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; + +// Typedef for our parsing of the auxv variables in /proc/pid/auxv. +#if defined(__i386) +typedef Elf32_auxv_t elf_aux_entry; +#elif defined(__x86_64__) +typedef Elf64_auxv_t elf_aux_entry; +#endif +// When we find the VDSO mapping in the process's address space, this +// is the name we use for it when writing it to the minidump. +// This should always be less than NAME_MAX! +const char kLinuxGateLibraryName[] = "linux-gate.so"; + +// We produce one of these structures for each thread in the crashed process. +struct ThreadInfo { + pid_t tgid; // thread group id + pid_t ppid; // parent process + + // Even on platforms where the stack grows down, the following will point to + // the smallest address in the stack. + const void* stack; // pointer to the stack area + size_t stack_len; // length of the stack to copy + + user_regs_struct regs; + user_fpregs_struct fpregs; +#if defined(__i386) + user_fpxregs_struct fpxregs; +#endif + +#if defined(__i386) || defined(__x86_64) + + static const unsigned kNumDebugRegisters = 8; + debugreg_t dregs[8]; +#endif +}; + +// One of these is produced for each mapping in the process (i.e. line in +// /proc/$x/maps). +struct MappingInfo { + uintptr_t start_addr; + size_t size; + size_t offset; // offset into the backed file. + char name[NAME_MAX]; +}; + +class LinuxDumper { + public: + explicit LinuxDumper(pid_t pid); + + // Parse the data for |threads| and |mappings|. + bool Init(); + + // Suspend/resume all threads in the given process. + bool ThreadsSuspend(); + bool ThreadsResume(); + + // Read information about the given thread. Returns true on success. One must + // have called |ThreadsSuspend| first. + bool ThreadInfoGet(pid_t tid, ThreadInfo* info); + + // These are only valid after a call to |Init|. + const wasteful_vector &threads() { return threads_; } + const wasteful_vector &mappings() { return mappings_; } + const MappingInfo* FindMapping(const void* address) const; + + // Find a block of memory to take as the stack given the top of stack pointer. + // stack: (output) the lowest address in the memory area + // stack_len: (output) the length of the memory area + // stack_top: the current top of the stack + bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); + + PageAllocator* allocator() { return &allocator_; } + + // memcpy from a remote process. + static void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Builds a proc path for a certain pid for a node. path is a + // character array that is overwritten, and node is the final node + // without any slashes. + void BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Utility method to find the location of where the kernel has + // mapped linux-gate.so in memory(shows up in /proc/pid/maps as + // [vdso], but we can't guarantee that it's the only virtual dynamic + // shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR + // is the safest way to go.) + void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const; + private: + bool EnumerateMappings(wasteful_vector* result) const; + bool EnumerateThreads(wasteful_vector* result) const; + + const pid_t pid_; + + mutable PageAllocator allocator_; + + bool threads_suspened_; + wasteful_vector threads_; // the ids of all the threads + wasteful_vector mappings_; // info from /proc//maps +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc new file mode 100644 index 000000000000..f5ed914b2d59 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2009, 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 + +#include "client/linux/minidump_writer/linux_dumper.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test LinuxDumperTest; +} + +TEST(LinuxDumperTest, Setup) { + LinuxDumper dumper(getpid()); +} + +TEST(LinuxDumperTest, FindMappings) { + LinuxDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(getpid))); + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(printf))); + ASSERT_FALSE(dumper.FindMapping(NULL)); +} + +TEST(LinuxDumperTest, ThreadList) { + LinuxDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_GE(dumper.threads().size(), 1); + bool found = false; + for (size_t i = 0; i < dumper.threads().size(); ++i) { + if (dumper.threads()[i] == getpid()) { + found = true; + break; + } + } +} + +TEST(LinuxDumperTest, BuildProcPath) { + const pid_t pid = getpid(); + LinuxDumper dumper(pid); + + char maps_path[256] = "dummymappath"; + char maps_path_expected[256]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "/proc/%d/maps", pid); + dumper.BuildProcPath(maps_path, pid, "maps"); + ASSERT_STREQ(maps_path, maps_path_expected); + + // In release mode, we expect BuildProcPath to handle the invalid + // parameters correctly and fill map_path with an empty + // NULL-terminated string. +#ifdef NDEBUG + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, 0, "maps"); + EXPECT_STREQ(maps_path, ""); + + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, getpid(), ""); + EXPECT_STREQ(maps_path, ""); + + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, getpid(), NULL); + EXPECT_STREQ(maps_path, ""); +#endif +} + +TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { + LinuxDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); + if (linux_gate_loc) { + bool found_linux_gate = false; + + const wasteful_vector mappings = dumper.mappings(); + const MappingInfo* mapping; + for (unsigned i = 0; i < mappings.size(); ++i) { + mapping = mappings[i]; + if (!strcmp(mapping->name, kLinuxGateLibraryName)) { + found_linux_gate = true; + break; + } + } + EXPECT_TRUE(found_linux_gate); + EXPECT_EQ(linux_gate_loc, reinterpret_cast(mapping->start_addr)); + EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); + } +} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc new file mode 100644 index 000000000000..079d85bdc50a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc @@ -0,0 +1,872 @@ +// Copyright (c) 2009, 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. + +// This code writes out minidump files: +// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx +// +// Minidumps are a Microsoft format which Breakpad uses for recording crash +// dumps. This code has to run in a compromised environment (the address space +// may have received SIGSEGV), thus the following rules apply: +// * You may not enter the dynamic linker. This means that we cannot call +// any symbols in a shared library (inc libc). Because of this we replace +// libc functions in linux_libc_support.h. +// * You may not call syscalls via the libc wrappers. This rule is a subset +// of the first rule but it bears repeating. We have direct wrappers +// around the system calls in linux_syscall_support.h. +// * You may not malloc. There's an alternative allocator in memory.h and +// a canonical instance in the LinuxDumper object. We use the placement +// new form to allocate objects and we don't delete them. + +#include "client/linux/minidump_writer/minidump_writer.h" +#include "client/minidump_file_writer-inl.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "client/minidump_file_writer.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/common/minidump_cpu_amd64.h" +#include "google_breakpad/common/minidump_cpu_x86.h" + +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "client/linux/minidump_writer//linux_dumper.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" + +// These are additional minidump stream values which are specific to the linux +// breakpad implementation. +enum { + MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */ + MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */ + MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */ + MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */ + MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */ + MD_LINUX_AUXV = 0x47670008 /* /proc/$x/auxv */ +}; + +// Minidump defines register structures which are different from the raw +// structures which we get from the kernel. These are platform specific +// functions to juggle the ucontext and user structures into minidump format. +#if defined(__i386) +typedef MDRawContextX86 RawContextCPU; + +// Write a uint16_t to memory +// out: memory location to write to +// v: value to write. +static void U16(void* out, uint16_t v) { + memcpy(out, &v, sizeof(v)); +} + +// Write a uint32_t to memory +// out: memory location to write to +// v: value to write. +static void U32(void* out, uint32_t v) { + memcpy(out, &v, sizeof(v)); +} + +// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format +// out: the minidump structure +// info: the collection of register structures. +static void CPUFillFromThreadInfo(MDRawContextX86 *out, + const google_breakpad::ThreadInfo &info) { + out->context_flags = MD_CONTEXT_X86_ALL; + + out->dr0 = info.dregs[0]; + out->dr1 = info.dregs[1]; + out->dr2 = info.dregs[2]; + out->dr3 = info.dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = info.dregs[6]; + out->dr7 = info.dregs[7]; + + out->gs = info.regs.xgs; + out->fs = info.regs.xfs; + out->es = info.regs.xes; + out->ds = info.regs.xds; + + out->edi = info.regs.edi; + out->esi = info.regs.esi; + out->ebx = info.regs.ebx; + out->edx = info.regs.edx; + out->ecx = info.regs.ecx; + out->eax = info.regs.eax; + + out->ebp = info.regs.ebp; + out->eip = info.regs.eip; + out->cs = info.regs.xcs; + out->eflags = info.regs.eflags; + out->esp = info.regs.esp; + out->ss = info.regs.xss; + + out->float_save.control_word = info.fpregs.cwd; + out->float_save.status_word = info.fpregs.swd; + out->float_save.tag_word = info.fpregs.twd; + out->float_save.error_offset = info.fpregs.fip; + out->float_save.error_selector = info.fpregs.fcs; + out->float_save.data_offset = info.fpregs.foo; + out->float_save.data_selector = info.fpregs.fos; + + // 8 registers * 10 bytes per register. + memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8); + + // This matches the Intel fpsave format. + U16(out->extended_registers + 0, info.fpregs.cwd); + U16(out->extended_registers + 2, info.fpregs.swd); + U16(out->extended_registers + 4, info.fpregs.twd); + U16(out->extended_registers + 6, info.fpxregs.fop); + U32(out->extended_registers + 8, info.fpxregs.fip); + U16(out->extended_registers + 12, info.fpxregs.fcs); + U32(out->extended_registers + 16, info.fpregs.foo); + U16(out->extended_registers + 20, info.fpregs.fos); + U32(out->extended_registers + 24, info.fpxregs.mxcsr); + + memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128); + memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128); +} + +// Juggle an x86 ucontext into minidump format +// out: the minidump structure +// info: the collection of register structures. +static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc, + const struct _libc_fpstate* fp) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_X86_FULL | + MD_CONTEXT_X86_FLOATING_POINT; + + out->gs = regs[REG_GS]; + out->fs = regs[REG_FS]; + out->es = regs[REG_ES]; + out->ds = regs[REG_DS]; + + out->edi = regs[REG_EDI]; + out->esi = regs[REG_ESI]; + out->ebx = regs[REG_EBX]; + out->edx = regs[REG_EDX]; + out->ecx = regs[REG_ECX]; + out->eax = regs[REG_EAX]; + + out->ebp = regs[REG_EBP]; + out->eip = regs[REG_EIP]; + out->cs = regs[REG_CS]; + out->eflags = regs[REG_EFL]; + out->esp = regs[REG_UESP]; + out->ss = regs[REG_SS]; + + out->float_save.control_word = fp->cw; + out->float_save.status_word = fp->sw; + out->float_save.tag_word = fp->tag; + out->float_save.error_offset = fp->ipoff; + out->float_save.error_selector = fp->cssel; + out->float_save.data_offset = fp->dataoff; + out->float_save.data_selector = fp->datasel; + + // 8 registers * 10 bytes per register. + memcpy(out->float_save.register_area, fp->_st, 10 * 8); +} + +#elif defined(__x86_64) +typedef MDRawContextAMD64 RawContextCPU; + +static void CPUFillFromThreadInfo(MDRawContextAMD64 *out, + const google_breakpad::ThreadInfo &info) { + out->context_flags = MD_CONTEXT_AMD64_FULL | + MD_CONTEXT_AMD64_SEGMENTS; + + out->cs = info.regs.cs; + + out->ds = info.regs.ds; + out->es = info.regs.es; + out->fs = info.regs.fs; + out->gs = info.regs.gs; + + out->ss = info.regs.ss; + out->eflags = info.regs.eflags; + + out->dr0 = info.dregs[0]; + out->dr1 = info.dregs[1]; + out->dr2 = info.dregs[2]; + out->dr3 = info.dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = info.dregs[6]; + out->dr7 = info.dregs[7]; + + out->rax = info.regs.rax; + out->rcx = info.regs.rcx; + out->rdx = info.regs.rdx; + out->rbx = info.regs.rbx; + + out->rsp = info.regs.rsp; + + out->rbp = info.regs.rbp; + out->rsi = info.regs.rsi; + out->rdi = info.regs.rdi; + out->r8 = info.regs.r8; + out->r9 = info.regs.r9; + out->r10 = info.regs.r10; + out->r11 = info.regs.r11; + out->r12 = info.regs.r12; + out->r13 = info.regs.r13; + out->r14 = info.regs.r14; + out->r15 = info.regs.r15; + + out->rip = info.regs.rip; + + out->flt_save.control_word = info.fpregs.cwd; + out->flt_save.status_word = info.fpregs.swd; + out->flt_save.tag_word = info.fpregs.ftw; + out->flt_save.error_opcode = info.fpregs.fop; + out->flt_save.error_offset = info.fpregs.rip; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_offset = info.fpregs.rdp; + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = info.fpregs.mxcsr; + out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask; + memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16); + memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16); +} + +static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc, + const struct _libc_fpstate* fpregs) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_AMD64_FULL; + + out->cs = regs[REG_CSGSFS] & 0xffff; + + out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; + out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; + + out->eflags = regs[REG_EFL]; + + out->rax = regs[REG_RAX]; + out->rcx = regs[REG_RCX]; + out->rdx = regs[REG_RDX]; + out->rbx = regs[REG_RBX]; + + out->rsp = regs[REG_RSP]; + out->rbp = regs[REG_RBP]; + out->rsi = regs[REG_RSI]; + out->rdi = regs[REG_RDI]; + out->r8 = regs[REG_R8]; + out->r9 = regs[REG_R9]; + out->r10 = regs[REG_R10]; + out->r11 = regs[REG_R11]; + out->r12 = regs[REG_R12]; + out->r13 = regs[REG_R13]; + out->r14 = regs[REG_R14]; + out->r15 = regs[REG_R15]; + + out->rip = regs[REG_RIP]; + + out->flt_save.control_word = fpregs->cwd; + out->flt_save.status_word = fpregs->swd; + out->flt_save.tag_word = fpregs->ftw; + out->flt_save.error_opcode = fpregs->fop; + out->flt_save.error_offset = fpregs->rip; + out->flt_save.data_offset = fpregs->rdp; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = fpregs->mxcsr; + out->flt_save.mx_csr_mask = fpregs->mxcr_mask; + memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); + memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); +} + +#else +#error "This code has not been ported to your platform yet." +#endif + +namespace google_breakpad { + +class MinidumpWriter { + public: + MinidumpWriter(const char* filename, + pid_t crashing_pid, + const ExceptionHandler::CrashContext* context) + : filename_(filename), + siginfo_(&context->siginfo), + ucontext_(&context->context), + float_state_(&context->float_state), + crashing_tid_(context->tid), + dumper_(crashing_pid) { + } + + bool Init() { + return dumper_.Init() && minidump_writer_.Open(filename_) && + dumper_.ThreadsSuspend(); + } + + ~MinidumpWriter() { + minidump_writer_.Close(); + dumper_.ThreadsResume(); + } + + bool Dump() { + // A minidump file contains a number of tagged streams. This is the number + // of stream which we write. + static const unsigned kNumWriters = 11; + + TypedMDRVA header(&minidump_writer_); + TypedMDRVA dir(&minidump_writer_); + if (!header.Allocate()) + return false; + if (!dir.AllocateArray(kNumWriters)) + return false; + memset(header.get(), 0, sizeof(MDRawHeader)); + + header.get()->signature = MD_HEADER_SIGNATURE; + header.get()->version = MD_HEADER_VERSION; + header.get()->time_date_stamp = time(NULL); + header.get()->stream_count = kNumWriters; + header.get()->stream_directory_rva = dir.position(); + + unsigned dir_index = 0; + MDRawDirectory dirent; + + if (!WriteThreadListStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteMappings(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteExceptionStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteSystemInfoStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CPU_INFO; + if (!WriteFile(&dirent.location, "/proc/cpuinfo")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_PROC_STATUS; + if (!WriteProcFile(&dirent.location, crashing_tid_, "status")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_LSB_RELEASE; + if (!WriteFile(&dirent.location, "/etc/lsb-release")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CMD_LINE; + if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_ENVIRON; + if (!WriteProcFile(&dirent.location, crashing_tid_, "environ")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_AUXV; + if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_AUXV; + if (!WriteProcFile(&dirent.location, crashing_tid_, "maps")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + // If you add more directory entries, don't forget to update kNumWriters, + // above. + + dumper_.ThreadsResume(); + return true; + } + + // Write information about the threads. + bool WriteThreadListStream(MDRawDirectory* dirent) { + const unsigned num_threads = dumper_.threads().size(); + + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) + return false; + + dirent->stream_type = MD_THREAD_LIST_STREAM; + dirent->location = list.location(); + + *list.get() = num_threads; + + for (unsigned i = 0; i < num_threads; ++i) { + MDRawThread thread; + my_memset(&thread, 0, sizeof(thread)); + thread.thread_id = dumper_.threads()[i]; + // We have a different source of information for the crashing thread. If + // we used the actual state of the thread we would find it running in the + // signal handler with the alternative stack, which would be deeply + // unhelpful. + if (thread.thread_id == crashing_tid_) { + const void* stack; + size_t stack_len; + if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer())) + return false; + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(stack_len)) + return false; + uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len); + dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len); + memory.Copy(stack_copy, stack_len); + thread.stack.start_of_memory_range = (uintptr_t) (stack); + thread.stack.memory = memory.location(); + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); + CPUFillFromUContext(cpu.get(), ucontext_, float_state_); + thread.thread_context = cpu.location(); + crashing_thread_context_ = cpu.location(); + } else { + ThreadInfo info; + if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info)) + return false; + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(info.stack_len)) + return false; + uint8_t* stack_copy = + (uint8_t*) dumper_.allocator()->Alloc(info.stack_len); + dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack, + info.stack_len); + memory.Copy(stack_copy, info.stack_len); + thread.stack.start_of_memory_range = (uintptr_t)(info.stack); + thread.stack.memory = memory.location(); + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); + CPUFillFromThreadInfo(cpu.get(), info); + thread.thread_context = cpu.location(); + } + + list.CopyIndexAfterObject(i, &thread, sizeof(thread)); + } + + return true; + } + + static bool ShouldIncludeMapping(const MappingInfo& mapping) { + if (mapping.name[0] == 0 || // we only want modules with filenames. + mapping.offset || // we only want to include one mapping per shared lib. + mapping.size < 4096) { // too small to get a signature for. + return false; + } + + return true; + } + + // Write information about the mappings in effect. Because we are using the + // minidump format, the information about the mappings is pretty limited. + // Because of this, we also include the full, unparsed, /proc/$x/maps file in + // another stream in the file. + bool WriteMappings(MDRawDirectory* dirent) { + const unsigned num_mappings = dumper_.mappings().size(); + unsigned num_output_mappings = 0; + + for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { + const MappingInfo& mapping = *dumper_.mappings()[i]; + if (ShouldIncludeMapping(mapping)) + num_output_mappings++; + } + + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) + return false; + + dirent->stream_type = MD_MODULE_LIST_STREAM; + dirent->location = list.location(); + *list.get() = num_output_mappings; + + for (unsigned i = 0, j = 0; i < num_mappings; ++i) { + const MappingInfo& mapping = *dumper_.mappings()[i]; + if (!ShouldIncludeMapping(mapping)) + continue; + + MDRawModule mod; + my_memset(&mod, 0, MD_MODULE_SIZE); + mod.base_of_image = mapping.start_addr; + mod.size_of_image = mapping.size; + const size_t filepath_len = my_strlen(mapping.name); + + // Figure out file name from path + const char* filename_ptr = mapping.name + filepath_len - 1; + while (filename_ptr >= mapping.name) { + if (*filename_ptr == '/') + break; + filename_ptr--; + } + filename_ptr++; + const size_t filename_len = mapping.name + filepath_len - filename_ptr; + + uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX]; + uint8_t* cv_ptr = cv_buf; + UntypedMDRVA cv(&minidump_writer_); + if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1)) + return false; + + const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE; + memcpy(cv_ptr, &cv_signature, sizeof(cv_signature)); + cv_ptr += sizeof(cv_signature); + + { + // We XOR the first page of the file to get a signature for it. + uint8_t xor_buf[sizeof(MDGUID)]; + size_t done = 0; + uint8_t* signature = cv_ptr; + cv_ptr += sizeof(xor_buf); + + my_memset(signature, 0, sizeof(xor_buf)); + while (done < 4096) { + dumper_.CopyFromProcess(xor_buf, crashing_tid_, + (void *) (mod.base_of_image + done), + sizeof(xor_buf)); + for (unsigned i = 0; i < sizeof(xor_buf); ++i) + signature[i] ^= xor_buf[i]; + done += sizeof(xor_buf); + } + my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux. + cv_ptr += sizeof(uint32_t); + } + + // Write pdb_file_name + memcpy(cv_ptr, filename_ptr, filename_len + 1); + cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1); + + mod.cv_record = cv.location(); + + MDLocationDescriptor ld; + if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) + return false; + mod.module_name_rva = ld.rva; + + list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); + } + + return true; + } + + bool WriteExceptionStream(MDRawDirectory* dirent) { + TypedMDRVA exc(&minidump_writer_); + if (!exc.Allocate()) + return false; + my_memset(exc.get(), 0, sizeof(MDRawExceptionStream)); + + dirent->stream_type = MD_EXCEPTION_STREAM; + dirent->location = exc.location(); + + exc.get()->thread_id = crashing_tid_; + exc.get()->exception_record.exception_code = siginfo_->si_signo; + exc.get()->exception_record.exception_address = + (uintptr_t) siginfo_->si_addr; + exc.get()->thread_context = crashing_thread_context_; + + return true; + } + + bool WriteSystemInfoStream(MDRawDirectory* dirent) { + TypedMDRVA si(&minidump_writer_); + if (!si.Allocate()) + return false; + my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); + + dirent->stream_type = MD_SYSTEM_INFO_STREAM; + dirent->location = si.location(); + + WriteCPUInformation(si.get()); + WriteOSInformation(si.get()); + + return true; + } + + private: +#if defined(__i386) + uintptr_t GetStackPointer() { + return ucontext_->uc_mcontext.gregs[REG_ESP]; + } +#elif defined(__x86_64) + uintptr_t GetStackPointer() { + return ucontext_->uc_mcontext.gregs[REG_RSP]; + } +#else +#error "This code has not been ported to your platform yet." +#endif + + void NullifyDirectoryEntry(MDRawDirectory* dirent) { + dirent->stream_type = 0; + dirent->location.data_size = 0; + dirent->location.rva = 0; + } + + bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; + static const char vendor_id_name[] = "vendor_id"; + static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; + + struct CpuInfoEntry { + const char* info_name; + int value; + bool found; + } cpu_info_table[] = { + { "processor", -1, false }, + { "model", 0, false }, + { "stepping", 0, false }, + { "cpu family", 0, false }, + }; + + // processor_architecture should always be set, do this first + sys_info->processor_architecture = +#if defined(__i386) + MD_CPU_ARCHITECTURE_X86; +#elif defined(__x86_64) + MD_CPU_ARCHITECTURE_AMD64; +#else +#error "Unknown CPU arch" +#endif + + const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); + if (fd < 0) + return false; + + { + PageAllocator allocator; + LineReader* const line_reader = new(allocator) LineReader(fd); + const char* line; + unsigned line_len; + while (line_reader->GetNextLine(&line, &line_len)) { + for (size_t i = 0; + i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); + i++) { + CpuInfoEntry* entry = &cpu_info_table[i]; + if (entry->found) + continue; + if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { + const char* value = strchr(line, ':'); + if (!value) + continue; + + // the above strncmp only matches the prefix, it might be the wrong + // line. i.e. we matched "model name" instead of "model". + // check and make sure there is only spaces between the prefix and + // the colon. + const char* space_ptr = line + strlen(entry->info_name); + for (; space_ptr < value; space_ptr++) { + if (!isspace(*space_ptr)) { + break; + } + } + if (space_ptr != value) + continue; + + sscanf(++value, " %d", &(entry->value)); + entry->found = true; + } + } + + // special case for vendor_id + if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { + const char* value = strchr(line, ':'); + if (!value) + goto popline; + + // skip ':" and all the spaces that follows + do { + value++; + } while (isspace(*value)); + + if (*value) { + size_t length = strlen(value); + if (length == 0) + goto popline; + // we don't want the trailing newline + if (value[length - 1] == '\n') + length--; + // ensure we have space for the value + if (length < sizeof(vendor_id)) + strncpy(vendor_id, value, length); + } + } + +popline: + line_reader->PopLine(line_len); + } + sys_close(fd); + } + + // make sure we got everything we wanted + for (size_t i = 0; + i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); + i++) { + if (!cpu_info_table[i].found) { + return false; + } + } + // /proc/cpuinfo contains cpu id, change it into number by adding one. + cpu_info_table[0].value++; + + sys_info->number_of_processors = cpu_info_table[0].value; + sys_info->processor_level = cpu_info_table[3].value; + sys_info->processor_revision = cpu_info_table[1].value << 8 | + cpu_info_table[2].value; + + if (vendor_id[0] != '\0') { + memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, + sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); + } + return true; + } + + bool WriteFile(MDLocationDescriptor* result, const char* filename) { + const int fd = sys_open(filename, O_RDONLY, 0); + if (fd < 0) + return false; + + // We can't stat the files because several of the files that we want to + // read are kernel seqfiles, which always have a length of zero. So we have + // to read as much as we can into a buffer. + static const unsigned kMaxFileSize = 1024; + uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize); + + size_t done = 0; + while (done < kMaxFileSize) { + ssize_t r; + do { + r = sys_read(fd, data + done, kMaxFileSize - done); + } while (r == -1 && errno == EINTR); + + if (r < 1) + break; + done += r; + } + sys_close(fd); + + if (!done) + return false; + + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(done)) + return false; + memory.Copy(data, done); + *result = memory.location(); + return true; + } + + bool WriteOSInformation(MDRawSystemInfo* sys_info) { + sys_info->platform_id = MD_OS_LINUX; + + struct utsname uts; + if (uname(&uts)) + return false; + + static const size_t buf_len = 512; + char buf[buf_len] = {0}; + size_t space_left = buf_len - 1; + const char* info_table[] = { + uts.sysname, + uts.release, + uts.version, + uts.machine, + NULL + }; + bool first_item = true; + for (const char** cur_info = info_table; *cur_info; cur_info++) { + static const char* separator = " "; + size_t separator_len = strlen(separator); + size_t info_len = strlen(*cur_info); + if (info_len == 0) + continue; + + if (space_left < info_len + (first_item ? 0 : separator_len)) + break; + + if (!first_item) { + strcat(buf, separator); + space_left -= separator_len; + } + + first_item = false; + strcat(buf, *cur_info); + space_left -= info_len; + } + + MDLocationDescriptor location; + if (!minidump_writer_.WriteString(buf, 0, &location)) + return false; + sys_info->csd_version_rva = location.rva; + + return true; + } + + bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, + const char* filename) { + char buf[80]; + memcpy(buf, "/proc/", 6); + const unsigned pid_len = my_int_len(pid); + my_itos(buf + 6, pid, pid_len); + buf[6 + pid_len] = '/'; + memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1); + return WriteFile(result, buf); + } + + const char* const filename_; // output filename + const siginfo_t* const siginfo_; // from the signal handler (see sigaction) + const struct ucontext* const ucontext_; // also from the signal handler + const struct _libc_fpstate* const float_state_; // ditto + const pid_t crashing_tid_; // the process which actually crashed + LinuxDumper dumper_; + MinidumpFileWriter minidump_writer_; + MDLocationDescriptor crashing_thread_context_; +}; + +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size) { + if (blob_size != sizeof(ExceptionHandler::CrashContext)) + return false; + const ExceptionHandler::CrashContext* context = + reinterpret_cast(blob); + MinidumpWriter writer(filename, crashing_process, context); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h similarity index 61% rename from toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h rename to toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h index 7c0511f5649c..579f68cdae79 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h @@ -1,8 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2009, Google Inc. // All rights reserved. // -// Author: Li Liu -// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,45 +27,27 @@ // (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 CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ -#define CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ #include - -#include "google_breakpad/common/breakpad_types.h" -#include "processor/scoped_ptr.h" - -struct sigcontext; +#include namespace google_breakpad { +// Write a minidump to the filesystem. This function does not malloc nor use +// libc functions which may. Thus, it can be used in contexts where the state +// of the heap may be corrupt. +// filename: the filename to write to. This is opened O_EXCL and fails if +// open fails. +// crashing_process: the pid of the crashing process. This must be trusted. +// blob: a blob of data from the crashing process. See exception_handler.h +// blob_size: the length of |blob|, in bytes // -// MinidumpGenerator -// -// Write a minidump to file based on the signo and sig_ctx. -// A minidump generator should be created before any exception happen. -// -class MinidumpGenerator { - public: - MinidumpGenerator(); - - ~MinidumpGenerator(); - - // Write minidump. - bool WriteMinidumpToFile(const char *file_pathname, - int signo, - uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) const; - private: - // Allocate memory for stack. - void AllocateStack(); - - private: - // Stack size of the writer thread. - static const int kStackSize = 1024 * 1024; - scoped_array stack_; -}; +// Returns true iff successful. +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size); } // namespace google_breakpad -#endif // CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ +#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc similarity index 57% rename from toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc rename to toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc index f8c4e78441b2..5ff336ce9720 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -1,8 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2009, Google Inc. // All rights reserved. // -// Author: Li Liu -// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,58 +27,53 @@ // (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 #include +#include -#include -#include -#include -#include - -#include "client/linux/handler/minidump_generator.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "breakpad_googletest_includes.h" using namespace google_breakpad; -// Thread use this to see if it should stop working. -static bool should_exit = false; +// This provides a wrapper around system calls which may be +// interrupted by a signal and return EINTR. See man 7 signal. +#define HANDLE_EINTR(x) ({ \ + typeof(x) __eintr_result__; \ + do { \ + __eintr_result__ = x; \ + } while (__eintr_result__ == -1 && errno == EINTR); \ + __eintr_result__;\ +}) -static void foo2(int arg) { - // Stack variable, used for debugging stack dumps. - int c = arg; - c = 0xcccccccc; - while (!should_exit) - sleep(1); +namespace { +typedef testing::Test MinidumpWriterTest; } -static void foo(int arg) { - // Stack variable, used for debugging stack dumps. - int b = arg; - b = 0xbbbbbbbb; - foo2(b); -} +TEST(MinidumpWriterTest, Setup) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); -static void *thread_main(void *) { - // Stack variable, used for debugging stack dumps. - int a = 0xaaaaaaaa; - foo(a); - return NULL; -} - -static void CreateThread(int num) { - pthread_t h; - for (int i = 0; i < num; ++i) { - pthread_create(&h, NULL, thread_main, NULL); - pthread_detach(h); + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); } -} + close(fds[0]); -int main(int argc, char *argv[]) { - CreateThread(10); - google_breakpad::MinidumpGenerator mg; - if (mg.WriteMinidumpToFile("minidump_test.out", -1, 0, NULL)) - printf("Succeeded written minidump\n"); - else - printf("Failed to write minidump\n"); - should_exit = true; - return 0; + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + + char templ[] = "/tmp/minidump-writer-unittest-XXXXXX"; + mktemp(templ); + ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context))); + struct stat st; + ASSERT_EQ(stat(templ, &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(templ); + + close(fds[1]); } diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc new file mode 100644 index 000000000000..da9661da25a8 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2009, 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 "common/linux/google_crashdump_uploader.h" +#include "third_party/linux/include/glog/logging.h" +#include "third_party/linux/include/gflags/gflags.h" +#include + +DEFINE_string(crash_server, "http://clients2.google.com/cr", + "The crash server to upload minidumps to."); +DEFINE_string(product_name, "", + "The product name that the minidump corresponds to."); +DEFINE_string(product_version, "", + "The version of the product that produced the minidump."); +DEFINE_string(client_id, "", + "The client GUID"); +DEFINE_string(minidump_path, "", + "The path of the minidump file."); +DEFINE_string(ptime, "", + "The process uptime in milliseconds."); +DEFINE_string(ctime, "", + "The cumulative process uptime in milliseconds."); +DEFINE_string(email, "", + "The user's email address."); +DEFINE_string(comments, "", + "Extra user comments"); +DEFINE_string(proxy_host, "", + "Proxy host"); +DEFINE_string(proxy_userpasswd, "", + "Proxy username/password in user:pass format."); + + +bool CheckForRequiredFlagsOrDie() { + std::string error_text = ""; + if (FLAGS_product_name.empty()) { + error_text.append("\nProduct name must be specified."); + } + + if (FLAGS_product_version.empty()) { + error_text.append("\nProduct version must be specified."); + } + + if (FLAGS_client_id.empty()) { + error_text.append("\nClient ID must be specified."); + } + + if (FLAGS_minidump_path.empty()) { + error_text.append("\nMinidump pathname must be specified."); + } + + if (!error_text.empty()) { + LOG(ERROR) << error_text; + return false; + } + return true; +} + +int main(int argc, char *argv[]) { + google::InitGoogleLogging(argv[0]); + google::ParseCommandLineFlags(&argc, &argv, true); + if (!CheckForRequiredFlagsOrDie()) { + return 1; + } + google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, + FLAGS_product_version, + FLAGS_client_id, + FLAGS_ptime, + FLAGS_ctime, + FLAGS_email, + FLAGS_comments, + FLAGS_minidump_path, + FLAGS_crash_server, + FLAGS_proxy_host, + FLAGS_proxy_userpasswd); + g.Upload(); +} diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj index 97659d3219ab..aaeb9a8ec8b7 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj @@ -19,6 +19,9 @@ F945858E0F782333009A47BF /* PBXTargetDependency */, F94585900F782336009A47BF /* PBXTargetDependency */, F93DE3A70F830D1D00608B94 /* PBXTargetDependency */, + F95BB8B3101F94D300AA053B /* PBXTargetDependency */, + F95BB8B5101F94D300AA053B /* PBXTargetDependency */, + F95BB8B7101F94D300AA053B /* PBXTargetDependency */, ); name = All; productName = All; @@ -88,6 +91,7 @@ F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE3400F82C68300608B94 /* exception_handler_test.cc */; }; F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; }; + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = F9B6309F100FF96B00D0F4AC /* goArrow.png */; }; F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; }; F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; }; F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; }; @@ -191,6 +195,55 @@ remoteGlobalIDString = F9C44DA40EF060A8003AEBAA; remoteInfo = BreakpadTest; }; + F95BB884101F949F00AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* crash_report */; + remoteInfo = crash_report; + }; + F95BB891101F94AC00AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* dump_syms */; + remoteInfo = dump_syms; + }; + F95BB89E101F94C000AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* symupload */; + remoteInfo = symupload; + }; + F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9BD835FB0B0544950055103E /* minidump_upload */; + remoteInfo = minidump_upload; + }; + F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* dump_syms */; + remoteInfo = dump_syms; + }; + F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* symupload */; + remoteInfo = symupload; + }; + F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* crash_report */; + remoteInfo = crash_report; + }; F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; @@ -288,6 +341,10 @@ F93DE3400F82C68300608B94 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = handler/exception_handler_test.cc; sourceTree = ""; }; F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = ""; }; F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = ""; }; + F95BB87C101F949F00AA053B /* crash_report.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = crash_report.xcodeproj; path = ../../tools/mac/crash_report/crash_report.xcodeproj; sourceTree = SOURCE_ROOT; }; + F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = dump_syms.xcodeproj; path = ../../tools/mac/dump_syms/dump_syms.xcodeproj; sourceTree = SOURCE_ROOT; }; + F95BB894101F94C000AA053B /* symupload.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = symupload.xcodeproj; path = ../../tools/mac/symupload/symupload.xcodeproj; sourceTree = SOURCE_ROOT; }; + F9B6309F100FF96B00D0F4AC /* goArrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goArrow.png; path = sender/goArrow.png; sourceTree = ""; }; F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = ""; }; F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = ""; }; @@ -404,6 +461,7 @@ 0867D691FE84028FC02AAC07 /* Breakpad */ = { isa = PBXGroup; children = ( + F95BB8A3101F94C300AA053B /* Tools */, 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */, F92C538D0ECCE6F2009BE4BA /* client */, F92C53600ECCE3D6009BE4BA /* common */, @@ -535,6 +593,7 @@ F92C56A60ECE04B6009BE4BA /* sender */ = { isa = PBXGroup; children = ( + F9B6309F100FF96B00D0F4AC /* goArrow.png */, F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, F945849C0F280E3C009A47BF /* Localizable.strings */, @@ -546,6 +605,41 @@ name = sender; sourceTree = ""; }; + F95BB87D101F949F00AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB885101F949F00AA053B /* crash_report */, + ); + name = Products; + sourceTree = ""; + }; + F95BB88A101F94AC00AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB892101F94AC00AA053B /* dump_syms */, + ); + name = Products; + sourceTree = ""; + }; + F95BB895101F94C000AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB89F101F94C000AA053B /* symupload */, + F95BB8A1101F94C000AA053B /* minidump_upload */, + ); + name = Products; + sourceTree = ""; + }; + F95BB8A3101F94C300AA053B /* Tools */ = { + isa = PBXGroup; + children = ( + F95BB894101F94C000AA053B /* symupload.xcodeproj */, + F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */, + F95BB87C101F949F00AA053B /* crash_report.xcodeproj */, + ); + name = Tools; + sourceTree = ""; + }; F9C44DAB0EF0726F003AEBAA /* testapp */ = { isa = PBXGroup; children = ( @@ -778,6 +872,20 @@ mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = F95BB87D101F949F00AA053B /* Products */; + ProjectRef = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + }, + { + ProductGroup = F95BB88A101F94AC00AA053B /* Products */; + ProjectRef = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + }, + { + ProductGroup = F95BB895101F94C000AA053B /* Products */; + ProjectRef = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + }, + ); projectRoot = ""; targets = ( 8DC2EF4F0486A6940098B216 /* Breakpad */, @@ -794,6 +902,37 @@ }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + F95BB885101F949F00AA053B /* crash_report */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = crash_report; + remoteRef = F95BB884101F949F00AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB892101F94AC00AA053B /* dump_syms */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dump_syms; + remoteRef = F95BB891101F94AC00AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB89F101F94C000AA053B /* symupload */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = symupload; + remoteRef = F95BB89E101F94C000AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB8A1101F94C000AA053B /* minidump_upload */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = minidump_upload; + remoteRef = F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ 8DC2EF520486A6940098B216 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -813,6 +952,7 @@ 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */, 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */, 3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */, + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1063,6 +1203,21 @@ target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */; targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */; }; + F95BB8B3101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = dump_syms; + targetProxy = F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */; + }; + F95BB8B5101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = symupload; + targetProxy = F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */; + }; + F95BB8B7101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = crash_report; + targetProxy = F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */; + }; F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8DC2EF4F0486A6940098B216 /* Breakpad */; diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h index ab4d6220057d..6b5ce66ac897 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h @@ -89,6 +89,7 @@ extern "C" { #define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" #define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" #define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" +#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" // Optional user-defined function to dec to decide if we should handle // this crash or forward it along. @@ -215,7 +216,7 @@ typedef bool (*BreakpadFilterCallback)(int exception_type, //============================================================================= // The following are NOT user-supplied but are documented here for // completeness. They are calculated by Breakpad during initialization & -// crash-dump generation. +// crash-dump generation, or entered in by the user. // // BREAKPAD_PROCESS_START_TIME The time the process started. // @@ -242,6 +243,12 @@ typedef bool (*BreakpadFilterCallback)(int exception_type, // server without leaking Breakpad's // internal values. // +// BREAKPAD_ON_DEMAND Used internally to indicate to the +// Reporter that we're sending on-demand, +// not as result of a crash. +// +// BREAKPAD_COMMENTS The text the user provided as comments. +// Only used in crash_report_sender. // Returns a new BreakpadRef object on success, NULL otherwise. BreakpadRef BreakpadCreate(NSDictionary *parameters); @@ -286,7 +293,7 @@ void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key); // necessary. Note that as mentioned above there are limits on both // the number of keys and their length. void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key, - NSString *value); + NSString *value); // This method will remove a previously-added parameter from the // upload parameter set. diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm index 551f97850999..23e5d9bfc6cb 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm @@ -409,7 +409,6 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; - NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL]; NSString *requestUserText = [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; @@ -451,7 +450,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { vendor = @"Vendor not specified"; } - // Normalize the values + // Normalize the values. if (skipConfirm) { skipConfirm = [skipConfirm uppercaseString]; @@ -504,7 +503,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { [resourcePath stringByAppendingPathComponent:@"Inspector"]; } - // Verify that there is an Inspector tool + // Verify that there is an Inspector tool. if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { DEBUGLOG(stderr, "Cannot find Inspector tool\n"); return false; @@ -517,7 +516,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath]; } - // Verify that there is a Reporter application + // Verify that there is a Reporter application. if (![[NSFileManager defaultManager] fileExistsAtPath:reporterPathString]) { DEBUGLOG(stderr, "Cannot find Reporter tool\n"); @@ -588,11 +587,6 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { } } - if (reportEmail) { - dictionary.SetKeyValue(BREAKPAD_EMAIL, - [reportEmail UTF8String]); - } - if (serverParameters) { // For each key-value pair, call BreakpadAddUploadParameter() NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; @@ -633,7 +627,9 @@ void Breakpad::RemoveKeyValue(NSString *key) { //============================================================================= void Breakpad::GenerateAndSendReport() { + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); HandleException(0, 0, 0, mach_thread_self()); + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); } //============================================================================= @@ -751,65 +747,59 @@ BreakpadRef BreakpadCreate(NSDictionary *parameters) { // Create a mutex for use in accessing the SimpleStringDictionary int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); - if (mutexResult != 0) { - throw mutexResult; // caught down below - } + if (mutexResult == 0) { - // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. - // Let's round up to the nearest page size. - // - int breakpad_pool_size = 4096; + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. + // Let's round up to the nearest page size. + // + int breakpad_pool_size = 4096; - /* - sizeof(Breakpad) - + sizeof(google_breakpad::ExceptionHandler) - + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) - */ + /* + sizeof(Breakpad) + + sizeof(google_breakpad::ExceptionHandler) + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) + */ - gBreakpadAllocator = - new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) - ProtectedMemoryAllocator(breakpad_pool_size); + gBreakpadAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(breakpad_pool_size); - // Stack-based autorelease pool for Breakpad::Create() obj-c code. - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - Breakpad *breakpad = Breakpad::Create(parameters); + // Stack-based autorelease pool for Breakpad::Create() obj-c code. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Breakpad *breakpad = Breakpad::Create(parameters); + + if (breakpad) { + // Make read-only to protect against memory smashers + gMasterAllocator->Protect(); + gKeyValueAllocator->Protect(); + gBreakpadAllocator->Protect(); + // Can uncomment this line to figure out how much space was actually + // allocated using this allocator + // printf("gBreakpadAllocator allocated size = %d\n", + // gBreakpadAllocator->GetAllocatedSize() ); + [pool release]; + return (BreakpadRef)breakpad; + } - if (breakpad) { - // Make read-only to protect against memory smashers - gMasterAllocator->Protect(); - gKeyValueAllocator->Protect(); - gBreakpadAllocator->Protect(); - } else { [pool release]; -#ifdef __EXCEPTIONS - throw(-1); -#else - return NULL; -#endif } - - // Can uncomment this line to figure out how much space was actually - // allocated using this allocator - // printf("gBreakpadAllocator allocated size = %d\n", - // gBreakpadAllocator->GetAllocatedSize() ); - - [pool release]; - return (BreakpadRef)breakpad; } catch(...) { // don't let exceptions leave this C API - if (gKeyValueAllocator) { - gKeyValueAllocator->~ProtectedMemoryAllocator(); - gKeyValueAllocator = NULL; - } - - if (gBreakpadAllocator) { - gBreakpadAllocator->~ProtectedMemoryAllocator(); - gBreakpadAllocator = NULL; - } - - delete gMasterAllocator; - gMasterAllocator = NULL; + fprintf(stderr, "BreakpadCreate() : error\n"); } + if (gKeyValueAllocator) { + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gKeyValueAllocator = NULL; + } + + if (gBreakpadAllocator) { + gBreakpadAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator = NULL; + } + + delete gMasterAllocator; + gMasterAllocator = NULL; + return NULL; } diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm index 5c5c2218dbdd..d328ee5dbf6b 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm @@ -319,6 +319,12 @@ kern_return_t Inspector::ReadMessages() { printf("parameter count = %d\n", info.parameter_count); #endif + // In certain situations where multiple crash requests come + // through quickly, we can end up with the mach IPC messages not + // coming through correctly. Since we don't know what parameters + // we've missed, we can't do much besides abort the crash dump + // situation in this case. + unsigned int parameters_read = 0; // The initial message contains the number of key value pairs that // we are expected to read. // Read each key/value pair, one mach message per key/value pair. @@ -329,6 +335,14 @@ kern_return_t Inspector::ReadMessages() { if(result == KERN_SUCCESS) { KeyValueMessageData &key_value_data = (KeyValueMessageData&)*message.GetData(); + // If we get a blank key, make sure we don't increment the + // parameter count; in some cases (notably on-demand generation + // many times in a short period of time) caused the Mach IPC + // messages to not come through correctly. + if (strlen(key_value_data.key) == 0) { + continue; + } + parameters_read++; config_params_.SetKeyValue(key_value_data.key, key_value_data.value); } else { @@ -336,6 +350,11 @@ kern_return_t Inspector::ReadMessages() { break; } } + if (parameters_read != info.parameter_count) { + DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash " + "dump generation.", parameters_read, info.parameter_count); + return KERN_FAILURE; + } } return result; @@ -379,7 +398,7 @@ bool Inspector::InspectTask() { SetCrashTimeParameters(); // If the client app has not specified a minidump directory, // use a default of Library// - if (0 == strlen(minidumpDirectory)) { + if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { NSArray *libraryDirectories = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc index 8c4a0d501f12..9886a5293d78 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc @@ -92,12 +92,15 @@ extern "C" boolean_t exc_server(mach_msg_header_t *request, mach_msg_header_t *reply); + // This symbol must be visible to dlsym() - see + // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details. kern_return_t catch_exception_raise(mach_port_t target_port, mach_port_t failed_thread, mach_port_t task, exception_type_t exception, exception_data_t code, - mach_msg_type_number_t code_count); + mach_msg_type_number_t code_count) + __attribute__((visibility("default"))); kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, @@ -435,6 +438,9 @@ kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { + if (task != mach_task_self()) { + return KERN_FAILURE; + } return ForwardException(task, failed_thread, exception, code, code_count); } diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc index e91a39520a58..f8dc8eaa3626 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc @@ -637,6 +637,7 @@ bool MinidumpGenerator::WriteSystemInfoStream( // get version and feature info cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, info_ptr->cpu.x86_cpu_info.feature_information); + // family info_ptr->processor_level = (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; @@ -644,6 +645,20 @@ bool MinidumpGenerator::WriteSystemInfoStream( info_ptr->processor_revision = (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4); + + // decode extended model info + if (info_ptr->processor_level == 0xF || + info_ptr->processor_level == 0x6) { + info_ptr->processor_revision |= + ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4); + } + + // decode extended family info + if (info_ptr->processor_level == 0xF) { + info_ptr->processor_level += + ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20); + } + #endif // __i386__ break; default: diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h index 8b4f327e1b24..b065fba956a3 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h @@ -68,7 +68,7 @@ typedef MDRawContextPPC MinidumpContext; // Use the REGISTER_FROM_THREADSTATE to access a register name from the // breakpad_thread_state_t structure. -#if __DARWIN_UNIX03 || !TARGET_CPU_X86 || TARGET_CPU_X86_64 +#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 // In The 10.5 SDK Headers Apple prepended __ to the variable names in the // i386_thread_state_t structure. There's no good way to tell what version of // the SDK we're compiling against so we just toggle on the same preprocessor diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib index ebc5ab40e73c..c9d43a3871a5 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib @@ -4,6 +4,14 @@ IBClasses + + CLASS + LengthLimitingTextField + LANGUAGE + ObjC + SUPERCLASS + NSTextField + ACTIONS @@ -26,10 +34,14 @@ NSButton commentMessage_ NSTextField + commentsEntryField_ + LengthLimitingTextField + countdownLabel_ + NSTextField dialogTitle_ NSTextField emailEntryField_ - NSView + LengthLimitingTextField emailLabel_ NSTextField emailMessage_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib index 21bd6319e32b..e39a8e54d339 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib @@ -3,17 +3,17 @@ IBFramework Version - 672 + 676 IBLastKnownRelativeProjectPath ../Breakpad.xcodeproj IBOldestOS 5 IBOpenObjects - 2 + 132 IBSystem Version - 9G55 + 9J61 targetFramework IBCocoaFramework diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib index d0cbd3f24e2a..e370206c1664 100644 Binary files a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib and b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib differ diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings index efc832acfbb8..70626567c95f 100644 Binary files a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings and b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings differ diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h index f62e7613c911..ca5b30797e63 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h @@ -41,6 +41,24 @@ extern NSString *const kGoogleServerType; extern NSString *const kSocorroServerType; extern NSString *const kDefaultServerType; + +// We're sublcassing NSTextField in order to override a particular +// method (see the implementation) that lets us reject changes if they +// are longer than a particular length. Bindings would normally solve +// this problem, but when we implemented a validation method, and +// returned NO for strings that were too long, the UI was not updated +// right away, which was a poor user experience. The UI would be +// updated as soon as the text field lost first responder status, +// which isn't soon enough. It is a known bug that the UI KVO didn't +// work in the middle of a validation. +@interface LengthLimitingTextField : NSTextField { + @private + unsigned int maximumLength_; +} + +- (void) setMaximumLength:(unsigned int)maxLength; +@end + @interface Reporter : NSObject { @public IBOutlet NSWindow *alertWindow_; // The alert window @@ -50,26 +68,34 @@ extern NSString *const kDefaultServerType; IBOutlet NSBox *preEmailBox_; IBOutlet NSBox *emailSectionBox_; // Localized elements (or things that need to be moved during localization). - IBOutlet NSTextField *dialogTitle_; - IBOutlet NSTextField *commentMessage_; - IBOutlet NSTextField *emailMessage_; - IBOutlet NSTextField *emailLabel_; - IBOutlet NSTextField *privacyLinkLabel_; - IBOutlet NSButton *sendButton_; - IBOutlet NSButton *cancelButton_; - IBOutlet NSView *emailEntryField_; - IBOutlet NSView *privacyLinkArrow_; + IBOutlet NSTextField *dialogTitle_; + IBOutlet NSTextField *commentMessage_; + IBOutlet NSTextField *emailMessage_; + IBOutlet NSTextField *emailLabel_; + IBOutlet NSTextField *privacyLinkLabel_; + IBOutlet NSButton *sendButton_; + IBOutlet NSButton *cancelButton_; + IBOutlet LengthLimitingTextField *emailEntryField_; + IBOutlet LengthLimitingTextField *commentsEntryField_; + IBOutlet NSTextField *countdownLabel_; + IBOutlet NSView *privacyLinkArrow_; // Text field bindings, for user input. NSString *commentsValue_; // Comments from the user NSString *emailValue_; // Email from the user - + NSString *countdownMessage_; // Message indicating time + // left for input. @private int configFile_; // File descriptor for config file NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) NSData *minidumpContents_; // The data in the minidump (STRONG) NSData *logFileData_; // An NSdata for the tar, // bz2'd log file. + NSTimeInterval remainingDialogTime_; // Keeps track of how long + // we have until we cancel + // the dialog + NSTimer *messageTimer_; // Timer we use to update + // the dialog NSMutableDictionary *serverDictionary_; // The dictionary mapping a // server type name to a // dictionary of server @@ -107,4 +133,7 @@ extern NSString *const kDefaultServerType; - (NSString *)emailValue; - (void)setEmailValue:(NSString *)value; +- (NSString *)countdownMessage; +- (void)setCountdownMessage:(NSString *)value; + @end diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m index 5d76290cef83..0cdeb67642e7 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m @@ -42,8 +42,11 @@ #define kLastSubmission @"LastSubmission" const int kMinidumpFileLengthLimit = 800000; +const int kUserCommentsMaxLength = 1500; +const int kEmailMaxLength = 64; -#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys" +#define kApplePrefsSyncExcludeAllKey \ + @"com.apple.PreferenceSync.ExcludeAllSyncKeys" NSString *const kGoogleServerType = @"google"; NSString *const kSocorroServerType = @"socorro"; @@ -109,9 +112,12 @@ NSString *const kDefaultServerType = @"google"; @implementation NSTextField (ResizabilityExtentions) - (float)breakpad_adjustHeightToFit { NSRect oldFrame = [self frame]; + // Starting with the 10.5 SDK, height won't grow, so make it huge to start. + NSRect presizeFrame = oldFrame; + presizeFrame.size.height = MAXFLOAT; // sizeToFit will blow out the width rather than making the field taller, so // we do it manually. - NSSize newSize = [[self cell] cellSizeForBounds:oldFrame]; + NSSize newSize = [[self cell] cellSizeForBounds:presizeFrame]; NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, NSWidth(oldFrame), newSize.height); [self setFrame:newFrame]; @@ -174,6 +180,9 @@ NSString *const kDefaultServerType = @"google"; // Returns YES if we should send the report without asking the user first. - (BOOL)shouldSubmitSilently; +// Returns YES if the minidump was generated on demand. +- (BOOL)isOnDemand; + // Returns YES if we should ask the user to provide comments. - (BOOL)shouldRequestComments; @@ -187,11 +196,11 @@ NSString *const kDefaultServerType = @"google"; // Returns the short description of the crash, suitable for use as a dialog // title (e.g., "The application Foo has quit unexpectedly"). -- (NSString*)shortCrashDialogMessage; +- (NSString*)shortDialogMessage; // Return explanatory text about the crash and the reporter, suitable for the // body text of a dialog. -- (NSString*)explanatoryCrashDialogText; +- (NSString*)explanatoryDialogText; // Returns the amount of time the UI should be shown before timing out. - (NSTimeInterval)messageTimeout; @@ -207,7 +216,7 @@ NSString *const kDefaultServerType = @"google"; - (void)removeEmailPrompt; // Run an alert window with the given timeout. Returns -// NSAlertButtonDefault if the timeout is exceeded. A timeout of 0 +// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0 // queues the message immediately in the modal run loop. - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout; @@ -235,6 +244,16 @@ NSString *const kDefaultServerType = @"google"; // will be uploaded to the crash server. - (void)addServerParameter:(id)value forKey:(NSString *)key; +// This method is used to periodically update the UI with how many +// seconds are left in the dialog display. +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer; + +// When we receive this notification, it means that the user has +// begun editing the email address or comments field, and we disable +// the timers so that the user has as long as they want to type +// in their comments/email. +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification; + @end @implementation Reporter @@ -261,6 +280,7 @@ NSString *const kDefaultServerType = @"google"; - (id)initWithConfigurationFD:(int)fd { if ((self = [super init])) { configFile_ = fd; + remainingDialogTime_ = 0; } // Because the reporter is embedded in the framework (and many copies @@ -545,8 +565,8 @@ NSString *const kDefaultServerType = @"google"; } } else { // Create an alert panel to tell the user something happened - NSPanel* alert = NSGetAlertPanel([self shortCrashDialogMessage], - [self explanatoryCrashDialogText], + NSPanel* alert = NSGetAlertPanel([self shortDialogMessage], + [self explanatoryDialogText], NSLocalizedString(@"sendReportButton", @""), NSLocalizedString(@"cancelButton", @""), nil); @@ -566,11 +586,11 @@ NSString *const kDefaultServerType = @"google"; // "fall" as text areas are shrunk from their overly-large IB sizes. // Localize the header. No resizing needed, as it has plenty of room. - [dialogTitle_ setStringValue:[self shortCrashDialogMessage]]; + [dialogTitle_ setStringValue:[self shortDialogMessage]]; // Localize the explanatory text field. [commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@", - [self explanatoryCrashDialogText], + [self explanatoryDialogText], NSLocalizedString(@"commentsMsg", @"")]]; float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit]; [headerBox_ breakpad_shiftVertically:commentHeightDelta]; @@ -615,9 +635,13 @@ NSString *const kDefaultServerType = @"google"; - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout { // Queue a |stopModal| message to be performed in |timeout| seconds. if (timeout > 0.001) { - [NSApp performSelector:@selector(stopModal) - withObject:nil - afterDelay:timeout]; + remainingDialogTime_ = timeout; + SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:); + messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0 + target:self + selector:updateSelector + userInfo:nil + repeats:YES]; } // Run the window modally and wait for either a |stopModal| message or a @@ -625,12 +649,6 @@ NSString *const kDefaultServerType = @"google"; [NSApp activateIgnoringOtherApps:YES]; int returnMethod = [NSApp runModalForWindow:window]; - // Cancel the pending |stopModal| message. - if (returnMethod != NSRunStoppedResponse) { - [NSObject cancelPreviousPerformRequestsWithTarget:NSApp - selector:@selector(stopModal) - object:nil]; - } return returnMethod; } @@ -667,8 +685,10 @@ NSString *const kDefaultServerType = @"google"; textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector { BOOL result = NO; - // If the user has entered text, don't end editing on "return" - if (commandSelector == @selector(insertNewline:) + // If the user has entered text on the comment field, don't end + // editing on "return". + if (control == commentsEntryField_ && + commandSelector == @selector(insertNewline:) && [[textView string] length] > 0) { [textView insertNewlineIgnoringFieldEditor:self]; result = YES; @@ -676,6 +696,50 @@ doCommandBySelector:(SEL)commandSelector { return result; } +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification { + [messageTimer_ invalidate]; + [self setCountdownMessage:@""]; +} + +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer { + remainingDialogTime_ -= 1; + + NSString *countdownMessage; + NSString *formatString; + + int displayedTimeLeft; // This can be either minutes or seconds. + + if (remainingDialogTime_ > 59) { + // calculate minutes remaining for UI purposes + displayedTimeLeft = (remainingDialogTime_ / 60); + + if (displayedTimeLeft == 1) { + formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @""); + } + } else { + displayedTimeLeft = remainingDialogTime_; + if (remainingDialogTime_ == 1) { + formatString = NSLocalizedString(@"countdownMsgSecondSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @""); + } + } + countdownMessage = [NSString stringWithFormat:formatString, + displayedTimeLeft]; + if (remainingDialogTime_ <= 30) { + [countdownLabel_ setTextColor:[NSColor redColor]]; + } + [self setCountdownMessage:countdownMessage]; + if (remainingDialogTime_ <= 0) { + [messageTimer_ invalidate]; + [NSApp stopModal]; + } +} + + + #pragma mark Accessors #pragma mark - //============================================================================= @@ -702,6 +766,17 @@ doCommandBySelector:(SEL)commandSelector { } } +- (NSString *)countdownMessage { + return [[countdownMessage_ retain] autorelease]; +} + +- (void)setCountdownMessage:(NSString *)value { + if (countdownMessage_ != value) { + [countdownMessage_ release]; + countdownMessage_ = [value copy]; + } +} + #pragma mark - //============================================================================= - (BOOL)reportIntervalElapsed { @@ -730,6 +805,11 @@ doCommandBySelector:(SEL)commandSelector { return YES; } +- (BOOL)isOnDemand { + return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND] + isEqualToString:@"YES"]; +} + - (BOOL)shouldSubmitSilently { return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]; @@ -745,21 +825,40 @@ doCommandBySelector:(SEL)commandSelector { isEqualToString:@"YES"]; } -- (NSString*)shortCrashDialogMessage { +- (NSString*)shortDialogMessage { NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; if (![displayName length]) displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; - return [NSString stringWithFormat:NSLocalizedString(@"headerFmt", @""), - displayName]; + if ([self isOnDemand]) { + return [NSString + stringWithFormat:NSLocalizedString(@"noCrashDialogHeader", @""), + displayName]; + } else { + return [NSString + stringWithFormat:NSLocalizedString(@"crashDialogHeader", @""), + displayName]; + } } -- (NSString*)explanatoryCrashDialogText { +- (NSString*)explanatoryDialogText { + NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + if (![displayName length]) + displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR]; if (![vendor length]) vendor = @"unknown vendor"; - return [NSString stringWithFormat:NSLocalizedString(@"msgFmt", @""), vendor]; + if ([self isOnDemand]) { + return [NSString + stringWithFormat:NSLocalizedString(@"noCrashDialogMsg", @""), + vendor, displayName]; + } else { + return [NSString + stringWithFormat:NSLocalizedString(@"crashDialogMsg", @""), + vendor]; + } } - (NSTimeInterval)messageTimeout { @@ -940,6 +1039,77 @@ doCommandBySelector:(SEL)commandSelector { [super dealloc]; } +- (void)awakeFromNib { + [emailEntryField_ setMaximumLength:kEmailMaxLength]; + [commentsEntryField_ setMaximumLength:kUserCommentsMaxLength]; +} + +@end + +//============================================================================= +@implementation LengthLimitingTextField + +- (void) setMaximumLength:(unsigned int)maxLength { + maximumLength_ = maxLength; +} + +// This is the method we're overriding in NSTextField, which lets us +// limit the user's input if it makes the string too long. +- (BOOL) textView:(NSTextView *)textView +shouldChangeTextInRange:(NSRange)affectedCharRange + replacementString:(NSString *)replacementString { + + // Sometimes the range comes in invalid, so reject if we can't + // figure out if the replacement text is too long. + if (affectedCharRange.location == NSNotFound) { + return NO; + } + // Figure out what the new string length would be, taking into + // account user selections. + int newStringLength = + [[textView string] length] - affectedCharRange.length + + [replacementString length]; + if (newStringLength > maximumLength_) { + return NO; + } else { + return YES; + } +} + +// Cut, copy, and paste have to be caught specifically since there is no menu. +- (BOOL)performKeyEquivalent:(NSEvent*)event { + // Only handle the key equivalent if |self| is the text field with focus. + NSText* fieldEditor = [self currentEditor]; + if (fieldEditor != nil) { + // Check for a single "Command" modifier + unsigned int modifiers = [event modifierFlags]; + modifiers &= NSDeviceIndependentModifierFlagsMask; + if (modifiers == NSCommandKeyMask) { + // Now, check for Select All, Cut, Copy, or Paste key equivalents. + NSString* characters = [event characters]; + // Select All is Command-A. + if ([characters isEqualToString:@"a"]) { + [fieldEditor selectAll:self]; + return YES; + // Cut is Command-X. + } else if ([characters isEqualToString:@"x"]) { + [fieldEditor cut:self]; + return YES; + // Copy is Command-C. + } else if ([characters isEqualToString:@"c"]) { + [fieldEditor copy:self]; + return YES; + // Paste is Command-V. + } else if ([characters isEqualToString:@"v"]) { + [fieldEditor paste:self]; + return YES; + } + } + } + // Let the super class handle the rest (e.g. Command-Period will cancel). + return [super performKeyEquivalent:event]; +} + @end //============================================================================= diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png new file mode 100644 index 000000000000..f318a56711d4 Binary files /dev/null and b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png differ diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm index cd163630d885..2ea103c6943e 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm @@ -209,8 +209,9 @@ const mach_port_t kNoLastExceptionThread = MACH_PORT_NULL; @"Last exception type is not 0 for on demand"); STAssertEquals(last_exception_code_, 0, @"Last exception code is not 0 for on demand"); - STAssertEquals(last_exception_thread_, (mach_port_t)0, - @"Last exception thread is not 0 for on demand"); + STAssertEquals(last_exception_thread_, mach_thread_self(), + @"Last exception thread is not mach_thread_self() " + "for on demand"); } @end diff --git a/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc b/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc index fdd591986e9b..354ffc90b755 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc @@ -37,6 +37,8 @@ #include #include +#include "common/linux/linux_syscall_support.h" +#include "common/linux/linux_libc_support.h" #include "client/minidump_file_writer-inl.h" #include "common/string_conversion.h" @@ -53,7 +55,11 @@ MinidumpFileWriter::~MinidumpFileWriter() { bool MinidumpFileWriter::Open(const char *path) { assert(file_ == -1); +#if __linux__ + file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#else file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#endif return file_ != -1; } @@ -63,7 +69,11 @@ bool MinidumpFileWriter::Close() { if (file_ != -1) { ftruncate(file_, position_); +#if __linux__ + result = (sys_close(file_) == 0); +#else result = (close(file_) == 0); +#endif file_ = -1; } @@ -227,9 +237,16 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) { return false; // Seek and write the data - if (lseek(file_, position, SEEK_SET) == static_cast(position)) - if (write(file_, src, size) == size) +#if __linux__ + if (sys_lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (sys_write(file_, src, size) == size) { +#else + if (lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (write(file_, src, size) == size) { +#endif return true; + } + } return false; } diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln b/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln index 8c9c02b2fe1d..d938a374c922 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln @@ -10,6 +10,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_report_sender", "send EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_generation", "crash_generation\crash_generation.vcproj", "{A820AF62-6239-4693-8430-4F516C1838F4}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler_test", "handler\exception_handler_test\exception_handler_test.vcproj", "{89094A11-CF25-4037-AF43-EACFA751405E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -42,6 +44,14 @@ Global {A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.Build.0 = Release|Win32 {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32 {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.ActiveCfg = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.Build.0 = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.ActiveCfg = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.Build.0 = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.ActiveCfg = Release|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.Build.0 = Release|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.ActiveCfg = Release|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc index ac76e590f5f2..dafa5c2acb11 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc @@ -218,12 +218,14 @@ bool CrashGenerationServer::Start() { } // Register a callback with the thread pool for the client connection. - RegisterWaitForSingleObject(&pipe_wait_handle_, - overlapped_.hEvent, - OnPipeConnected, - this, - INFINITE, - kPipeIOThreadFlags); + if (!RegisterWaitForSingleObject(&pipe_wait_handle_, + overlapped_.hEvent, + OnPipeConnected, + this, + INFINITE, + kPipeIOThreadFlags)) { + return false; + } pipe_ = CreateNamedPipe(pipe_name_.c_str(), kPipeAttr, diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc index 48900273d39f..be9e5eb21ad8 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc @@ -249,12 +249,12 @@ ExceptionHandler::~ExceptionHandler() { // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the // system's application event log. fprintf(stderr, "warning: removing Breakpad handler out of order\n"); - for (vector::iterator iterator = - handler_stack_->begin(); - iterator != handler_stack_->end(); - ++iterator) { + vector::iterator iterator = handler_stack_->begin(); + while (iterator != handler_stack_->end()) { if (*iterator == this) { - handler_stack_->erase(iterator); + iterator = handler_stack_->erase(iterator); + } else { + ++iterator; } } } diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc new file mode 100644 index 000000000000..2b7377ea5785 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc @@ -0,0 +1,164 @@ +// Copyright 2009, 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 "breakpad_googletest_includes.h" +#include "client/windows/crash_generation/crash_generation_server.h" +#include "client/windows/handler/exception_handler.h" +#include +#include +#include +#include +#include + +namespace { +const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer"; +const char kSuccessIndicator[] = "success"; +const char kFailureIndicator[] = "failure"; + +// Utility function to test for a path's existence. +BOOL DoesPathExist(const TCHAR *path_name); + +class ExceptionHandlerDeathTest : public ::testing::Test { +protected: + // Member variable for each test that they can use + // for temporary storage. + TCHAR temp_path_[MAX_PATH]; + // Actually constructs a temp path name. + virtual void SetUp(); + // A helper method that tests can use to crash. + void DoCrash(); +}; + +void ExceptionHandlerDeathTest::SetUp() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + TCHAR temp_path[MAX_PATH] = { '\0' }; + TCHAR test_name_wide[MAX_PATH] = { '\0' }; + // We want the temporary directory to be what the OS returns + // to us, + the test case name. + GetTempPath(MAX_PATH, temp_path); + // THe test case name is exposed to use as a c-style string, + // But we might be working in UNICODE here on Windows. + int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(), + (int)strlen(test_info->name()), test_name_wide, MAX_PATH); + if (!dwRet) { + assert(false); + } + StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide); + CreateDirectory(temp_path_, NULL); +} + +BOOL DoesPathExist(const TCHAR *path_name) { + DWORD flags = GetFileAttributes(path_name); + if (flags == INVALID_FILE_ATTRIBUTES) { + return FALSE; + } + return TRUE; +} + +bool MinidumpWrittenCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + if (succeeded && DoesPathExist(dump_path)) { + fprintf(stderr, kSuccessIndicator); + } else { + fprintf(stderr, kFailureIndicator); + } + // If we don't flush, the output doesn't get sent before + // this process dies. + fflush(stderr); + return succeeded; +} + +TEST_F(ExceptionHandlerDeathTest, InProcTest) { + // For the in-proc test, we just need to instantiate an exception + // handler in in-proc mode, and crash. Since the entire test is + // reexecuted in the child process, we don't have to worry about + // the semantics of the exception handler being inherited/not + // inherited across CreateProcess(). + ASSERT_TRUE(DoesPathExist(temp_path_)); + google_breakpad::ExceptionHandler *exc = + new google_breakpad::ExceptionHandler( + temp_path_, NULL, &MinidumpWrittenCallback, NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL); + int *i = NULL; + ASSERT_DEATH((*i)++, kSuccessIndicator); + delete exc; +} + +static bool gDumpCallbackCalled = false; + +void clientDumpCallback(void *dump_context, + const google_breakpad::ClientInfo *client_info, + const std::wstring *dump_path){ + + gDumpCallbackCalled = true; +} + +void ExceptionHandlerDeathTest::DoCrash() { + google_breakpad::ExceptionHandler *exc = + new google_breakpad::ExceptionHandler( + temp_path_, NULL, NULL, NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName, + NULL); + // Although this is executing in the child process of the death test, + // if it's not true we'll still get an error rather than the crash + // being expected. + ASSERT_TRUE(exc->IsOutOfProcess()); + int *i = NULL; + printf("%d\n", (*i)++); +} + +TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) { + // We can take advantage of a detail of google test here to save some + // complexity in testing: when you do a death test, it actually forks. + // So we can make the main test harness the crash generation server, + // and call ASSERT_DEATH on a NULL dereference, it to expecting test + // the out of process scenario, since it's happening in a different + // process! This is different from the above because, above, we pass + // a NULL pipe name, and we also don't start a crash generation server. + + ASSERT_TRUE(DoesPathExist(temp_path_)); + std::wstring dump_path(temp_path_); + google_breakpad::CrashGenerationServer server( + kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true, + &dump_path); + + // This HAS to be EXPECT_, because when this test case is executed in the + // child process, the server registration will fail due to the named pipe + // being the same. + EXPECT_TRUE(server.Start()); + EXPECT_FALSE(gDumpCallbackCalled); + ASSERT_DEATH(this->DoCrash(), ""); + EXPECT_TRUE(gDumpCallbackCalled); +} +} \ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj new file mode 100644 index 000000000000..4708d88e94f2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc index a3c560bd5d0c..5dfe40510272 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc @@ -183,7 +183,7 @@ bool ShowDumpResults(const wchar_t* dump_path, delete [] text; } - AppendTextWorker(text); + QueueUserWorkItem(AppendTextWorker, text, WT_EXECUTEDEFAULT); return succeeded; } @@ -467,6 +467,7 @@ int APIENTRY _tWinMain(HINSTANCE instance, CustomClientInfo custom_info = {kCustomInfoEntries, kCustomInfoCount}; + CrashServerStart(); // This is needed for CRT to not show dialog for invalid param // failures and instead let the code handle it. _CrtSetReportMode(_CRT_ASSERT, 0); diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc index 046c7b346b5c..a78881899a45 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc @@ -27,114 +27,48 @@ // (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 -#include -#include -#include +#include #include #include #include #include #include +#include #include -#include #include #include #include -#include +#include +#include +#include +#include +#include #include #include +#include +#include #include -#include #include "common/linux/dump_symbols.h" #include "common/linux/file_id.h" -#include "common/linux/guid_creator.h" -#include "processor/scoped_ptr.h" +#include "common/linux/module.h" +#include "common/linux/stabs_reader.h" // This namespace contains helper functions. namespace { -// Infomation of a line. -struct LineInfo { - // The index into string table for the name of the source file which - // this line belongs to. - // Load from stab symbol. - uint32_t source_name_index; - // Offset from start of the function. - // Load from stab symbol. - ElfW(Off) rva_to_func; - // Offset from base of the loading binary. - ElfW(Off) rva_to_base; - // Size of the line. - // It is the difference of the starting address of the line and starting - // address of the next N_SLINE, N_FUN or N_SO. - uint32_t size; - // Line number. - uint32_t line_num; - // Id of the source file for this line. - int source_id; -}; - -typedef std::list LineInfoList; - -// Information of a function. -struct FuncInfo { - // Name of the function. - const char *name; - // Offset from the base of the loading address. - ElfW(Off) rva_to_base; - // Virtual address of the function. - // Load from stab symbol. - ElfW(Addr) addr; - // Size of the function. - // It is the difference of the starting address of the function and starting - // address of the next N_FUN or N_SO. - uint32_t size; - // Total size of stack parameters. - uint32_t stack_param_size; - // Is there any lines included from other files? - bool has_sol; - // Line information array. - LineInfoList line_info; -}; - -typedef std::list FuncInfoList; - -// Information of a source file. -struct SourceFileInfo { - // Name string index into the string table. - uint32_t name_index; - // Name of the source file. - const char *name; - // Starting address of the source file. - ElfW(Addr) addr; - // Id of the source file. - int source_id; - // Functions information. - FuncInfoList func_info; -}; - -typedef std::list SourceFileInfoList; - -// Information of a symbol table. -// This is the root of all types of symbol. -struct SymbolInfo { - SourceFileInfoList source_file_info; - - // The next source id for newly found source file. - int next_source_id; -}; +using google_breakpad::Module; +using std::vector; // Stab section name. static const char *kStabName = ".stab"; // Demangle using abi call. // Older GCC may not support it. -static std::string Demangle(const char *mangled) { +static std::string Demangle(const std::string &mangled) { int status = 0; - char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status); + char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); if (status == 0 && demangled != NULL) { std::string str(demangled); free(demangled); @@ -146,7 +80,7 @@ static std::string Demangle(const char *mangled) { // Fix offset into virtual address by adding the mapped base into offsets. // Make life easier when want to find something by offset. static void FixAddress(void *obj_base) { - ElfW(Word) base = reinterpret_cast(obj_base); + ElfW(Addr) base = reinterpret_cast(obj_base); ElfW(Ehdr) *elf_header = static_cast(obj_base); elf_header->e_phoff += base; elf_header->e_shoff += base; @@ -187,384 +121,219 @@ static const ElfW(Shdr) *FindSectionByName(const char *name, for (int i = 0; i < nsection; ++i) { const char *section_name = - (char*)(strtab->sh_offset + sections[i].sh_name); + reinterpret_cast(strtab->sh_offset + sections[i].sh_name); if (!strncmp(name, section_name, name_len)) return sections + i; } return NULL; } -// TODO(liuli): Computer the stack parameter size. -// Expect parameter variables are immediately following the N_FUN symbol. -// Will need to parse the type information to get a correct size. -static int LoadStackParamSize(struct nlist *list, - struct nlist *list_end, - struct FuncInfo *func_info) { - struct nlist *cur_list = list; - assert(cur_list->n_type == N_FUN); - ++cur_list; - int step = 1; - while (cur_list < list_end && cur_list->n_type == N_PSYM) { - ++cur_list; - ++step; - } - func_info->stack_param_size = 0; - return step; -} +// Our handler class for STABS data. +class DumpStabsHandler: public google_breakpad::StabsHandler { + public: + DumpStabsHandler(Module *module) : + module_(module), + comp_unit_base_address_(0), + current_function_(NULL), + current_source_file_(NULL), + current_source_file_name_(NULL) { } -static int LoadLineInfo(struct nlist *list, - struct nlist *list_end, - const struct SourceFileInfo &source_file_info, - struct FuncInfo *func_info) { - struct nlist *cur_list = list; - func_info->has_sol = false; - // Records which source file the following lines belongs. Default - // to the file we are handling. This helps us handling inlined source. - // When encountering N_SOL, we will change this to the source file - // specified by N_SOL. - int current_source_name_index = source_file_info.name_index; - do { - // Skip non line information. - while (cur_list < list_end && cur_list->n_type != N_SLINE) { - // Only exit when got another function, or source file. - if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO) - return cur_list - list; - // N_SOL means source lines following it will be from - // another source file. - if (cur_list->n_type == N_SOL) { - func_info->has_sol = true; + bool StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory); + bool EndCompilationUnit(uint64_t address); + bool StartFunction(const std::string &name, uint64_t address); + bool EndFunction(uint64_t address); + bool Line(uint64_t address, const char *name, int number); - if (cur_list->n_un.n_strx > 0 && - cur_list->n_un.n_strx != current_source_name_index) { - // The following lines will be from this source file. - current_source_name_index = cur_list->n_un.n_strx; - } - } - ++cur_list; - } - struct LineInfo line; - while (cur_list < list_end && cur_list->n_type == N_SLINE) { - line.source_name_index = current_source_name_index; - line.rva_to_func = cur_list->n_value; - // n_desc is a signed short - line.line_num = (unsigned short)cur_list->n_desc; - // Don't set it here. - // Will be processed in later pass. - line.source_id = -1; - func_info->line_info.push_back(line); - ++cur_list; - } - } while (list < list_end); + // Do any final processing necessary to make module_ contain all the + // data provided by the STABS reader. + // + // Because STABS does not provide reliable size information for + // functions and lines, we need to make a pass over the data after + // processing all the STABS to compute those sizes. We take care of + // that here. + void Finalize(); - return cur_list - list; -} + private: -static int LoadFuncSymbols(struct nlist *list, - struct nlist *list_end, - const ElfW(Shdr) *stabstr_section, - struct SourceFileInfo *source_file_info) { - struct nlist *cur_list = list; - assert(cur_list->n_type == N_SO); - ++cur_list; - source_file_info->func_info.clear(); - while (cur_list < list_end) { - // Go until the function symbol. - while (cur_list < list_end && cur_list->n_type != N_FUN) { - if (cur_list->n_type == N_SO) { - return cur_list - list; - } - ++cur_list; - continue; - } - if (cur_list->n_type == N_FUN) { - struct FuncInfo func_info; - func_info.name = - reinterpret_cast(cur_list->n_un.n_strx + - stabstr_section->sh_offset); - func_info.addr = cur_list->n_value; - func_info.rva_to_base = 0; - func_info.size = 0; - func_info.stack_param_size = 0; - func_info.has_sol = 0; + // An arbitrary, but very large, size to use for functions whose + // size we can't compute properly. + static const uint64_t kFallbackSize = 0x10000000; - // Stack parameter size. - cur_list += LoadStackParamSize(cur_list, list_end, &func_info); - // Line info. - cur_list += LoadLineInfo(cur_list, - list_end, - *source_file_info, - &func_info); + // The module we're contributing debugging info to. + Module *module_; - // Functions in this module should have address bigger than the module - // startring address. - // There maybe a lot of duplicated entry for a function in the symbol, - // only one of them can met this. - if (func_info.addr >= source_file_info->addr) { - source_file_info->func_info.push_back(func_info); - } - } - } - return cur_list - list; -} + // The functions we've generated so far. We don't add these to + // module_ as we parse them. Instead, we wait until we've computed + // their ending address, and their lines' ending addresses. + // + // We could just stick them in module_ from the outset, but if + // module_ already contains data gathered from other debugging + // formats, that would complicate the size computation. + vector functions_; -// Comapre the address. -// The argument should have a memeber named "addr" -template -static bool CompareAddress(T1 *a, T2 *b) { - return a->addr < b->addr; -} + // Boundary addresses. STABS doesn't necessarily supply sizes for + // functions and lines, so we need to compute them ourselves by + // finding the next object. + vector boundaries_; -// Sort the array into increasing ordered array based on the virtual address. -// Return vector of pointers to the elements in the incoming array. So caller -// should make sure the returned vector lives longer than the incoming vector. -template -static std::vector SortByAddress( - Container *container) { - typedef typename Container::iterator It; - typedef typename Container::value_type T; - std::vector sorted_array_ptr; - sorted_array_ptr.reserve(container->size()); - for (It it = container->begin(); it != container->end(); it++) - sorted_array_ptr.push_back(&(*it)); - std::sort(sorted_array_ptr.begin(), - sorted_array_ptr.end(), - std::ptr_fun(CompareAddress)); + // The base address of the current compilation unit. We use this to + // recognize functions we should omit from the symbol file. (If you + // know the details of why we omit these, please patch this + // comment.) + Module::Address comp_unit_base_address_; - return sorted_array_ptr; -} + // The function we're currently contributing lines to. + Module::Function *current_function_; -// Find the address of the next function or source file symbol in the symbol -// table. The address should be bigger than the current function's address. -static ElfW(Addr) NextAddress( - std::vector *sorted_functions, - std::vector *sorted_files, - const struct FuncInfo &func_info) { - std::vector::iterator next_func_iter = - std::find_if(sorted_functions->begin(), - sorted_functions->end(), - std::bind1st( - std::ptr_fun( - CompareAddress - ), - &func_info) - ); - if (next_func_iter != sorted_functions->end()) - return (*next_func_iter)->addr; + // The last Module::File we got a line number in. + Module::File *current_source_file_; - std::vector::iterator next_file_iter = - std::find_if(sorted_files->begin(), - sorted_files->end(), - std::bind1st( - std::ptr_fun( - CompareAddress - ), - &func_info) - ); - if (next_file_iter != sorted_files->end()) { - return (*next_file_iter)->addr; - } - return 0; -} - -static int FindFileByNameIdx(uint32_t name_index, - SourceFileInfoList &files) { - for (SourceFileInfoList::iterator it = files.begin(); - it != files.end(); it++) { - if (it->name_index == name_index) - return it->source_id; - } - - return -1; -} - -// Add included file information. -// Also fix the source id for the line info. -static void AddIncludedFiles(struct SymbolInfo *symbols, - const ElfW(Shdr) *stabstr_section) { - for (SourceFileInfoList::iterator source_file_it = - symbols->source_file_info.begin(); - source_file_it != symbols->source_file_info.end(); - ++source_file_it) { - struct SourceFileInfo &source_file = *source_file_it; - - for (FuncInfoList::iterator func_info_it = source_file.func_info.begin(); - func_info_it != source_file.func_info.end(); - ++func_info_it) { - struct FuncInfo &func_info = *func_info_it; - - for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); - line_info_it != func_info.line_info.end(); ++line_info_it) { - struct LineInfo &line_info = *line_info_it; - - assert(line_info.source_name_index > 0); - assert(source_file.name_index > 0); - - // Check if the line belongs to the source file by comparing the - // name index into string table. - if (line_info.source_name_index != source_file.name_index) { - // This line is not from the current source file, check if this - // source file has been added before. - int found_source_id = FindFileByNameIdx(line_info.source_name_index, - symbols->source_file_info); - if (found_source_id < 0) { - // Got a new included file. - // Those included files don't have address or line information. - SourceFileInfo new_file; - new_file.name_index = line_info.source_name_index; - new_file.name = reinterpret_cast(new_file.name_index + - stabstr_section->sh_offset); - new_file.addr = 0; - new_file.source_id = symbols->next_source_id++; - line_info.source_id = new_file.source_id; - symbols->source_file_info.push_back(new_file); - } else { - // The file has been added. - line_info.source_id = found_source_id; - } - } else { - // The line belongs to the file. - line_info.source_id = source_file.source_id; - } - } // for each line. - } // for each function. - } // for each source file. - -} - -// Compute size and rva information based on symbols loaded from stab section. -static bool ComputeSizeAndRVA(ElfW(Addr) loading_addr, - struct SymbolInfo *symbols) { - std::vector sorted_files = - SortByAddress(&(symbols->source_file_info)); - for (size_t i = 0; i < sorted_files.size(); ++i) { - struct SourceFileInfo &source_file = *sorted_files[i]; - std::vector sorted_functions = - SortByAddress(&(source_file.func_info)); - for (size_t j = 0; j < sorted_functions.size(); ++j) { - struct FuncInfo &func_info = *sorted_functions[j]; - assert(func_info.addr >= loading_addr); - func_info.rva_to_base = func_info.addr - loading_addr; - func_info.size = 0; - ElfW(Addr) next_addr = NextAddress(&sorted_functions, - &sorted_files, - func_info); - // I've noticed functions with an address bigger than any other functions - // and source files modules, this is probably the last function in the - // module, due to limitions of Linux stab symbol, it is impossible to get - // the exact size of this kind of function, thus we give it a default - // very big value. This should be safe since this is the last function. - // But it is a ugly hack..... - // The following code can reproduce the case: - // template - // void Foo(T value) { - // } - // - // int main(void) { - // Foo(10); - // Foo(std::string("hello")); - // return 0; - // } - // TODO(liuli): Find a better solution. - static const int kDefaultSize = 0x10000000; - static int no_next_addr_count = 0; - if (next_addr != 0) { - func_info.size = next_addr - func_info.addr; - } else { - if (no_next_addr_count > 1) { - fprintf(stderr, "Got more than one funtion without the \ - following symbol. Igore this function.\n"); - fprintf(stderr, "The dumped symbol may not correct.\n"); - assert(!"This should not happen!\n"); - func_info.size = 0; - continue; - } - - no_next_addr_count++; - func_info.size = kDefaultSize; - } - // Compute line size. - for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); - line_info_it != func_info.line_info.end(); line_info_it++) { - struct LineInfo &line_info = *line_info_it; - LineInfoList::iterator next_line_info_it = line_info_it; - next_line_info_it++; - line_info.size = 0; - if (next_line_info_it != func_info.line_info.end()) { - line_info.size = - next_line_info_it->rva_to_func - line_info.rva_to_func; - } else { - // The last line in the function. - // If we can find a function or source file symbol immediately - // following the line, we can get the size of the line by computing - // the difference of the next address to the starting address of this - // line. - // Otherwise, we need to set a default big enough value. This occurs - // mostly because the this function is the last one in the module. - if (next_addr != 0) { - ElfW(Off) next_addr_offset = next_addr - func_info.addr; - line_info.size = next_addr_offset - line_info.rva_to_func; - } else { - line_info.size = kDefaultSize; - } - } - line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base; - } // for each line. - } // for each function. - } // for each source file. + // The pointer in the .stabstr section of the name that + // current_source_file_ is built from. This allows us to quickly + // recognize when the current line is in the same file as the + // previous one (which it usually is). + const char *current_source_file_name_; +}; + +bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory) { + assert(! comp_unit_base_address_); + current_source_file_name_ = name; + current_source_file_ = module_->FindFile(name); + comp_unit_base_address_ = address; + boundaries_.push_back(static_cast(address)); return true; } +bool DumpStabsHandler::EndCompilationUnit(uint64_t address) { + assert(comp_unit_base_address_); + comp_unit_base_address_ = 0; + current_source_file_ = NULL; + current_source_file_name_ = NULL; + if (address) + boundaries_.push_back(static_cast(address)); + return true; +} + +bool DumpStabsHandler::StartFunction(const std::string &name, + uint64_t address) { + assert(! current_function_); + Module::Function *f = new Module::Function; + f->name_ = Demangle(name); + f->address_ = address; + f->size_ = 0; // We compute this in DumpStabsHandler::Finalize(). + f->parameter_size_ = 0; // We don't provide this information. + current_function_ = f; + boundaries_.push_back(static_cast(address)); + return true; +} + +bool DumpStabsHandler::EndFunction(uint64_t address) { + assert(current_function_); + // Functions in this compilation unit should have address bigger + // than the compilation unit's starting address. There may be a lot + // of duplicated entries for functions in the STABS data; only one + // entry can meet this requirement. + // + // (I don't really understand the above comment; just bringing it + // along from the previous code, and leaving the behaivor unchanged. + // If you know the whole story, please patch this comment. --jimb) + if (current_function_->address_ >= comp_unit_base_address_) + functions_.push_back(current_function_); + else + delete current_function_; + current_function_ = NULL; + if (address) + boundaries_.push_back(static_cast(address)); + return true; +} + +bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) { + assert(current_function_); + assert(current_source_file_); + if (name != current_source_file_name_) { + current_source_file_ = module_->FindFile(name); + current_source_file_name_ = name; + } + Module::Line line; + line.address_ = address; + line.size_ = 0; // We compute this in DumpStabsHandler::Finalize(). + line.file_ = current_source_file_; + line.number_ = number; + current_function_->lines_.push_back(line); + return true; +} + +void DumpStabsHandler::Finalize() { + // Sort our boundary list, so we can search it quickly. + sort(boundaries_.begin(), boundaries_.end()); + // Sort all functions by address, just for neatness. + sort(functions_.begin(), functions_.end(), + Module::Function::CompareByAddress); + for (vector::iterator func_it = functions_.begin(); + func_it != functions_.end(); + func_it++) { + Module::Function *f = *func_it; + // Compute the function f's size. + vector::iterator boundary + = std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address_); + if (boundary != boundaries_.end()) + f->size_ = *boundary - f->address_; + else + // If this is the last function in the module, and the STABS + // reader was unable to give us its ending address, then assign + // it a bogus, very large value. This will happen at most once + // per module: since we've added all functions' addresses to the + // boundary table, only one can be the last. + f->size_ = kFallbackSize; + + // Compute sizes for each of the function f's lines --- if it has any. + if (! f->lines_.empty()) { + stable_sort(f->lines_.begin(), f->lines_.end(), + Module::Line::CompareByAddress); + vector::iterator last_line = f->lines_.end() - 1; + for (vector::iterator line_it = f->lines_.begin(); + line_it != last_line; line_it++) + line_it[0].size_ = line_it[1].address_ - line_it[0].address_; + // Compute the size of the last line from f's end address. + last_line->size_ = (f->address_ + f->size_) - last_line->address_; + } + } + // Now that everything has a size, add our functions to the module, and + // dispose of our private list. + module_->AddFunctions(functions_.begin(), functions_.end()); + functions_.clear(); +} + static bool LoadSymbols(const ElfW(Shdr) *stab_section, const ElfW(Shdr) *stabstr_section, - ElfW(Addr) loading_addr, - struct SymbolInfo *symbols) { + Module *module) { if (stab_section == NULL || stabstr_section == NULL) return false; - struct nlist *lists = - reinterpret_cast(stab_section->sh_offset); - int nstab = stab_section->sh_size / sizeof(struct nlist); - // First pass, load all symbols from the object file. - for (int i = 0; i < nstab; ) { - int step = 1; - struct nlist *cur_list = lists + i; - if (cur_list->n_type == N_SO) { - // FUNC
- struct SourceFileInfo source_file_info; - source_file_info.name_index = cur_list->n_un.n_strx; - source_file_info.name = reinterpret_cast(cur_list->n_un.n_strx + - stabstr_section->sh_offset); - source_file_info.addr = cur_list->n_value; - if (strchr(source_file_info.name, '.')) - source_file_info.source_id = symbols->next_source_id++; - else - source_file_info.source_id = -1; - step = LoadFuncSymbols(cur_list, lists + nstab, - stabstr_section, &source_file_info); - symbols->source_file_info.push_back(source_file_info); - } - i += step; - } - - // Second pass, compute the size of functions and lines. - if (ComputeSizeAndRVA(loading_addr, symbols)) { - // Third pass, check for included source code, especially for header files. - // Until now, we only have compiling unit information, but they can - // have code from include files, add them here. - AddIncludedFiles(symbols, stabstr_section); - return true; - } - return false; + // A callback object to handle data from the STABS reader. + DumpStabsHandler handler(module); + // Find the addresses of the STABS data, and create a STABS reader object. + uint8_t *stabs = reinterpret_cast(stab_section->sh_offset); + uint8_t *stabstr = reinterpret_cast(stabstr_section->sh_offset); + google_breakpad::StabsReader reader(stabs, stab_section->sh_size, + stabstr, stabstr_section->sh_size, + &handler); + // Read the STABS data, and do post-processing. + if (! reader.Process()) + return false; + handler.Finalize(); + return true; } -static bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) { +static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) { // Translate all offsets in section headers into address. FixAddress(elf_header); ElfW(Addr) loading_addr = GetLoadingAddress( reinterpret_cast(elf_header->e_phoff), elf_header->e_phnum); + module->SetLoadAddress(loading_addr); const ElfW(Shdr) *sections = reinterpret_cast(elf_header->e_shoff); @@ -578,107 +347,7 @@ static bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) { const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections; // Load symbols. - return LoadSymbols(stab_section, stabstr_section, loading_addr, symbols); -} - -static bool WriteModuleInfo(FILE *file, - ElfW(Half) arch, - const std::string &obj_file) { - const char *arch_name = NULL; - if (arch == EM_386) - arch_name = "x86"; - else if (arch == EM_X86_64) - arch_name = "x86_64"; - else - return false; - - unsigned char identifier[16]; - google_breakpad::FileID file_id(obj_file.c_str()); - if (file_id.ElfFileIdentifier(identifier)) { - char identifier_str[40]; - file_id.ConvertIdentifierToString(identifier, - identifier_str, sizeof(identifier_str)); - char id_no_dash[40]; - int id_no_dash_len = 0; - memset(id_no_dash, 0, sizeof(id_no_dash)); - for (int i = 0; identifier_str[i] != '\0'; ++i) - if (identifier_str[i] != '-') - id_no_dash[id_no_dash_len++] = identifier_str[i]; - // Add an extra "0" by the end. - id_no_dash[id_no_dash_len++] = '0'; - std::string filename = obj_file; - size_t slash_pos = obj_file.find_last_of("/"); - if (slash_pos != std::string::npos) - filename = obj_file.substr(slash_pos + 1); - return 0 <= fprintf(file, "MODULE Linux %s %s %s\n", arch_name, - id_no_dash, filename.c_str()); - } - return false; -} - -static bool WriteSourceFileInfo(FILE *file, const struct SymbolInfo &symbols) { - for (SourceFileInfoList::const_iterator it = - symbols.source_file_info.begin(); - it != symbols.source_file_info.end(); it++) { - if (it->source_id != -1) { - const char *name = it->name; - if (0 > fprintf(file, "FILE %d %s\n", it->source_id, name)) - return false; - } - } - return true; -} - -static bool WriteOneFunction(FILE *file, - const struct FuncInfo &func_info){ - // Discard the ending part of the name. - std::string func_name(func_info.name); - std::string::size_type last_colon = func_name.find_last_of(':'); - if (last_colon != std::string::npos) - func_name = func_name.substr(0, last_colon); - func_name = Demangle(func_name.c_str()); - - if (func_info.size <= 0) - return true; - - if (0 <= fprintf(file, "FUNC %lx %lx %d %s\n", - (unsigned long) func_info.rva_to_base, - (unsigned long) func_info.size, - func_info.stack_param_size, - func_name.c_str())) { - for (LineInfoList::const_iterator it = func_info.line_info.begin(); - it != func_info.line_info.end(); it++) { - const struct LineInfo &line_info = *it; - if (0 > fprintf(file, "%lx %lx %d %d\n", - (unsigned long) line_info.rva_to_base, - (unsigned long) line_info.size, - line_info.line_num, - line_info.source_id)) - return false; - } - return true; - } - return false; -} - -static bool WriteFunctionInfo(FILE *file, const struct SymbolInfo &symbols) { - for (SourceFileInfoList::const_iterator it = - symbols.source_file_info.begin(); - it != symbols.source_file_info.end(); it++) { - const struct SourceFileInfo &file_info = *it; - for (FuncInfoList::const_iterator fiIt = file_info.func_info.begin(); - fiIt != file_info.func_info.end(); fiIt++) { - const struct FuncInfo &func_info = *fiIt; - if (!WriteOneFunction(file, func_info)) - return false; - } - } - return true; -} - -static bool DumpStabSymbols(FILE *file, const struct SymbolInfo &symbols) { - return WriteSourceFileInfo(file, symbols) && - WriteFunctionInfo(file, symbols); + return LoadSymbols(stab_section, stabstr_section, module); } // @@ -733,6 +402,48 @@ class MmapWrapper { size_t size_; }; +// Return the breakpad symbol file identifier for the architecture of +// ELF_HEADER. +const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) { + ElfW(Half) arch = elf_header->e_machine; + if (arch == EM_386) + return "x86"; + else if (arch == EM_X86_64) + return "x86_64"; + else + return NULL; +} + +// Format the Elf file identifier in IDENTIFIER as a UUID with the +// dashes removed. +std::string FormatIdentifier(unsigned char identifier[16]) { + char identifier_str[40]; + google_breakpad::FileID::ConvertIdentifierToString( + identifier, + identifier_str, + sizeof(identifier_str)); + std::string id_no_dash; + for (int i = 0; identifier_str[i] != '\0'; ++i) + if (identifier_str[i] != '-') + id_no_dash += identifier_str[i]; + // Add an extra "0" by the end. PDB files on Windows have an 'age' + // number appended to the end of the file identifier; this isn't + // really used or necessary on other platforms, but let's preserve + // the pattern. + id_no_dash += '0'; + return id_no_dash; +} + +// Return the non-directory portion of FILENAME: the portion after the +// last slash, or the whole filename if there are no slashes. +std::string BaseFileName(const std::string &filename) { + // Lots of copies! basename's behavior is less than ideal. + char *c_filename = strdup(filename.c_str()); + std::string base = basename(c_filename); + free(c_filename); + return base; +} + } // namespace namespace google_breakpad { @@ -754,17 +465,27 @@ bool DumpSymbols::WriteSymbolFile(const std::string &obj_file, ElfW(Ehdr) *elf_header = reinterpret_cast(obj_base); if (!IsValidElf(elf_header)) return false; - struct SymbolInfo symbols; - symbols.next_source_id = 0; - if (!LoadSymbols(elf_header, &symbols)) - return false; - // Write to symbol file. - if (WriteModuleInfo(sym_file, elf_header->e_machine, obj_file) && - DumpStabSymbols(sym_file, symbols)) - return true; + unsigned char identifier[16]; + google_breakpad::FileID file_id(obj_file.c_str()); + if (! file_id.ElfFileIdentifier(identifier)) + return false; - return false; + const char *architecture = ElfArchitecture(elf_header); + if (! architecture) + return false; + + std::string name = BaseFileName(obj_file); + std::string os = "Linux"; + std::string id = FormatIdentifier(identifier); + + Module module(name, os, architecture, id); + if (!LoadSymbols(elf_header, &module)) + return false; + if (!module.Write(sym_file)) + return false; + + return true; } } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc index 1adf2a1354a2..34c9e5080242 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc @@ -32,104 +32,74 @@ // See file_id.h for documentation // -#include -#include +#include "common/linux/file_id.h" + +#include #include #include #include -#include #include +#include #include -#include "common/linux/file_id.h" -#include "common/md5.h" +#include +#include namespace google_breakpad { -static bool FindElfTextSection(const void *elf_mapped_base, - const void **text_start, - int *text_size) { - assert(elf_mapped_base); - assert(text_start); - assert(text_size); - - const unsigned char *elf_base = - static_cast(elf_mapped_base); - const ElfW(Ehdr) *elf_header = - reinterpret_cast(elf_base); - if (memcmp(elf_header, ELFMAG, SELFMAG) != 0) - return false; - *text_start = NULL; - *text_size = 0; - const ElfW(Shdr) *sections = - reinterpret_cast(elf_base + elf_header->e_shoff); - const char *text_section_name = ".text"; - int name_len = strlen(text_section_name); - const ElfW(Shdr) *string_section = sections + elf_header->e_shstrndx; - const ElfW(Shdr) *text_section = NULL; - for (int i = 0; i < elf_header->e_shnum; ++i) { - if (sections[i].sh_type == SHT_PROGBITS) { - const char *section_name = (char*)(elf_base + - string_section->sh_offset + - sections[i].sh_name); - if (!strncmp(section_name, text_section_name, name_len)) { - text_section = §ions[i]; - break; - } - } - } - if (text_section != NULL && text_section->sh_size > 0) { - int text_section_size = text_section->sh_size; - *text_start = elf_base + text_section->sh_offset; - *text_size = text_section_size; - } - return true; -} - -FileID::FileID(const char *path) { +FileID::FileID(const char* path) { strncpy(path_, path, sizeof(path_)); } -bool FileID::ElfFileIdentifier(unsigned char identifier[16]) { +bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { + const ssize_t mapped_len = 4096; // Page size (matches WriteMappings()) int fd = open(path_, O_RDONLY); if (fd < 0) return false; struct stat st; - if (fstat(fd, &st) != 0 && st.st_size <= 0) { + if (fstat(fd, &st) != 0 || st.st_size <= mapped_len) { close(fd); return false; } - void *base = mmap(NULL, st.st_size, + void* base = mmap(NULL, mapped_len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (base == MAP_FAILED) { - close(fd); + close(fd); + if (base == MAP_FAILED) return false; - } - bool success = false; - const void *text_section = NULL; - int text_size = 0; - if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) { - struct MD5Context md5; - MD5Init(&md5); - MD5Update(&md5, - static_cast(text_section), - text_size); - MD5Final(identifier, &md5); - success = true; + + memset(identifier, 0, kMDGUIDSize); + uint8_t* ptr = reinterpret_cast(base); + uint8_t* ptr_end = ptr + mapped_len; + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) + identifier[i] ^= ptr[i]; + ptr += kMDGUIDSize; } - close(fd); - munmap(base, st.st_size); - return success; + munmap(base, mapped_len); + return true; } // static -void FileID::ConvertIdentifierToString(const unsigned char identifier[16], - char *buffer, int buffer_length) { +void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], + char* buffer, int buffer_length) { + uint8_t identifier_swapped[kMDGUIDSize]; + + // Endian-ness swap to match dump processor expectation. + memcpy(identifier_swapped, identifier, kMDGUIDSize); + uint32_t* data1 = reinterpret_cast(identifier_swapped); + *data1 = htonl(*data1); + uint16_t* data2 = reinterpret_cast(identifier_swapped + 4); + *data2 = htons(*data2); + uint16_t* data3 = reinterpret_cast(identifier_swapped + 6); + *data3 = htons(*data3); + int buffer_idx = 0; - for (int idx = 0; (buffer_idx < buffer_length) && (idx < 16); ++idx) { - int hi = (identifier[idx] >> 4) & 0x0F; - int lo = (identifier[idx]) & 0x0F; + for (unsigned int idx = 0; + (buffer_idx < buffer_length) && (idx < kMDGUIDSize); + ++idx) { + int hi = (identifier_swapped[idx] >> 4) & 0x0F; + int lo = (identifier_swapped[idx]) & 0x0F; if (idx == 4 || idx == 6 || idx == 8 || idx == 10) buffer[buffer_idx++] = '-'; diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h index 5e1cd6e1a001..31bb5e4a5555 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h @@ -35,25 +35,30 @@ #include +#include "common/linux/guid_creator.h" + namespace google_breakpad { +static const size_t kMDGUIDSize = sizeof(MDGUID); + class FileID { public: - FileID(const char *path); - ~FileID() {}; + explicit FileID(const char* path); + ~FileID() {} // Load the identifier for the elf file path specified in the constructor into // |identifier|. Return false if the identifier could not be created for the // file. - // The current implementation will return the MD5 hash of the file's bytes. - bool ElfFileIdentifier(unsigned char identifier[16]); + // The current implementation will XOR the first page of data to generate an + // identifier. + bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]); // Convert the |identifier| data to a NULL terminated string. The string will // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE). // The |buffer| should be at least 37 bytes long to receive all of the data // and termination. Shorter buffers will contain truncated data. - static void ConvertIdentifierToString(const unsigned char identifier[16], - char *buffer, int buffer_length); + static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], + char* buffer, int buffer_length); private: // Storage for the path specified @@ -63,4 +68,3 @@ class FileID { } // namespace google_breakpad #endif // COMMON_LINUX_FILE_ID_H__ - diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc new file mode 100644 index 000000000000..f47a8e5ecdd3 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2009, 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 "common/linux/google_crashdump_uploader.h" +#include "common/linux/libcurl_wrapper.h" +#include "third_party/linux/include/glog/logging.h" + +#include +#include +#include + +namespace google_breakpad { + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword) { + LibcurlWrapper* http_layer = new LibcurlWrapper(); + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer) { + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +void GoogleCrashdumpUploader::Init(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer) { + product_ = product; + version_ = version; + guid_ = guid; + ptime_ = ptime; + ctime_ = ctime; + email_ = email; + comments_ = comments; + http_layer_ = http_layer; + + crash_server_ = crash_server; + proxy_host_ = proxy_host; + proxy_userpassword_ = proxy_userpassword; + minidump_pathname_ = minidump_pathname; + LOG(INFO) << "Uploader initializing"; + LOG(INFO) << "\tProduct: " << product_; + LOG(INFO) << "\tVersion: " << version_; + LOG(INFO) << "\tGUID: " << guid_; + if (!ptime_.empty()) { + LOG(INFO) << "\tProcess uptime: " << ptime_; + } + if (!ctime_.empty()) { + LOG(INFO) << "\tCumulative Process uptime: " << ctime_; + } + if (!email_.empty()) { + LOG(INFO) << "\tEmail: " << email_; + } + if (!comments_.empty()) { + LOG(INFO) << "\tComments: " << comments_; + } +} + +bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() { + std::string error_text; + if (product_.empty()) { + error_text.append("\nProduct name must be specified."); + } + + if (version_.empty()) { + error_text.append("\nProduct version must be specified."); + } + + if (guid_.empty()) { + error_text.append("\nClient ID must be specified."); + } + + if (minidump_pathname_.empty()) { + error_text.append("\nMinidump pathname must be specified."); + } + + if (!error_text.empty()) { + LOG(ERROR) << error_text; + return false; + } + return true; + +} + +bool GoogleCrashdumpUploader::Upload() { + bool ok = http_layer_->Init(); + if (!ok) { + LOG(WARNING) << "http layer init failed"; + return ok; + } + + if (!CheckRequiredParametersArePresent()) { + return false; + } + + struct stat st; + int err = stat(minidump_pathname_.c_str(), &st); + if (err) { + LOG(WARNING) << minidump_pathname_ << " could not be found: " << errno; + return false; + } + + parameters_["prod"] = product_; + parameters_["ver"] = version_; + parameters_["guid"] = guid_; + parameters_["ptime"] = ptime_; + parameters_["ctime"] = ctime_; + parameters_["email"] = email_; + parameters_["comments_"] = comments_; + if (!http_layer_->AddFile(minidump_pathname_, + "upload_file_minidump")) { + return false; + } + LOG(INFO) << "Sending request to " << crash_server_; + return http_layer_->SendRequest(crash_server_, + parameters_, + NULL); +} +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h new file mode 100644 index 000000000000..5cea17d995e7 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h @@ -0,0 +1,98 @@ +// Copyright (c) 2009, 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 +#include + +namespace google_breakpad { + +class LibcurlWrapper; + +class GoogleCrashdumpUploader { + public: + GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword); + + GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer); + + void Init(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer); + bool Upload(); + + private: + bool CheckRequiredParametersArePresent(); + + LibcurlWrapper* http_layer_; + std::string product_; + std::string version_; + std::string guid_; + std::string ptime_; + std::string ctime_; + std::string email_; + std::string comments_; + std::string minidump_pathname_; + + std::string crash_server_; + std::string proxy_host_; + std::string proxy_userpassword_; + + std::map parameters_; +}; +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc new file mode 100644 index 000000000000..c65355c98e21 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc @@ -0,0 +1,166 @@ +// Copyright (c) 2009, 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. + +// Unit test for crash dump uploader. + +#include "common/linux/google_crashdump_uploader.h" +#include "common/linux/libcurl_wrapper.h" +#include "breakpad_googletest_includes.h" + +namespace google_breakpad { + +using ::testing::Return; +using ::testing::_; + +class MockLibcurlWrapper : public LibcurlWrapper { + public: + MOCK_METHOD0(Init, bool()); + MOCK_METHOD2(SetProxy, bool(const std::string& proxy_host, + const std::string& proxy_userpwd)); + MOCK_METHOD2(AddFile, bool(const std::string& upload_file_path, + const std::string& basename)); + MOCK_METHOD3(SendRequest, + bool(const std::string& url, + const std::map& parameters, + std::string* server_response)); +}; + +class GoogleCrashdumpUploaderTest : public ::testing::Test { +}; + +TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload()); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { + // Create a temp file + char tempfn[80] = "/tmp/googletest-upload-XXXXXX"; + int fd = mkstemp(tempfn); + ASSERT_NE(fd, -1); + close(fd); + + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true)); + EXPECT_CALL(m, + SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + tempfn, + "http://foo.com", + "", + "", + &m); + ASSERT_TRUE(uploader->Upload()); +} + + +TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, SendRequest(_,_,_)).Times(0); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload()); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { + // Test with empty product name. + GoogleCrashdumpUploader uploader("", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + ""); + ASSERT_FALSE(uploader.Upload()); + + // Test with empty product version. + GoogleCrashdumpUploader uploader1("product", + "", + "AAA-BBB", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + + ASSERT_FALSE(uploader1.Upload()); + + // Test with empty client GUID. + GoogleCrashdumpUploader uploader2("product", + "1.0", + "", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + ASSERT_FALSE(uploader2.Upload()); +} +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc new file mode 100644 index 000000000000..5bea3afa262f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2009, 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 +#include +#include +#include + +#include + +#include "common/linux/libcurl_wrapper.h" +#include "third_party/linux/include/glog/logging.h" + +namespace google_breakpad { +LibcurlWrapper::LibcurlWrapper() + : init_ok_(false), + formpost_(NULL), + lastptr_(NULL), + headerlist_(NULL) { + curl_lib_ = dlopen("libcurl.so", RTLD_NOW); + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW); + } + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW); + } + if (!curl_lib_) { + LOG(WARNING) << "Could not find libcurl via dlopen"; + return; + } + LOG(INFO) << "LibcurlWrapper init succeeded"; + init_ok_ = true; + return; +} + +bool LibcurlWrapper::SetProxy(const std::string& proxy_host, + const std::string& proxy_userpwd) { + if (!init_ok_) { + return false; + } + // Set proxy information if necessary. + if (!proxy_host.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str()); + } else { + LOG(WARNING) << "SetProxy called with empty proxy host."; + return false; + } + if (!proxy_userpwd.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str()); + } else { + LOG(WARNING) << "SetProxy called with empty proxy username/password."; + return false; + } + LOG(INFO) << "Set proxy host to " << proxy_host; + return true; +} + +bool LibcurlWrapper::AddFile(const std::string& upload_file_path, + const std::string& basename) { + if (!init_ok_) { + return false; + } + LOG(INFO) << "Adding " << upload_file_path << " to form upload."; + // Add form file. + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, basename.c_str(), + CURLFORM_FILE, upload_file_path.c_str(), + CURLFORM_END); + + return true; +} + +// Callback to get the response data from server. +static size_t WriteCallback(void *ptr, size_t size, + size_t nmemb, void *userp) { + if (!userp) + return 0; + + std::string *response = reinterpret_cast(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast(ptr), real_size); + return real_size; +} + +bool LibcurlWrapper::SendRequest(const std::string& url, + const std::map& parameters, + std::string* server_response) { + (*easy_setopt_)(curl_, CURLOPT_URL, url.c_str()); + std::map::const_iterator iter = parameters.begin(); + for (; iter != parameters.end(); ++iter) + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, iter->first.c_str(), + CURLFORM_COPYCONTENTS, iter->second.c_str(), + CURLFORM_END); + + (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_); + if (server_response != NULL) { + (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); + (*easy_setopt_)(curl_, CURLOPT_WRITEDATA, + reinterpret_cast(server_response)); + } + + CURLcode err_code = CURLE_OK; + err_code = (*easy_perform_)(curl_); + *(void**) (&easy_strerror_) = dlsym(curl_lib_, "curl_easy_strerror"); +#ifndef NDEBUG + if (err_code != CURLE_OK) + fprintf(stderr, "Failed to send http request to %s, error: %s\n", + url.c_str(), + (*easy_strerror_)(err_code)); +#endif + if (headerlist_ != NULL) { + (*slist_free_all_)(headerlist_); + } + + (*easy_cleanup_)(curl_); + if (formpost_ != NULL) { + (*formfree_)(formpost_); + } + + return err_code == CURLE_OK; +} + +bool LibcurlWrapper::Init() { + if (!init_ok_) { + LOG(WARNING) << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages"; + return false; + } + + if (!SetFunctionPointers()) { + LOG(WARNING) << "Could not find function pointers"; + init_ok_ = false; + return false; + } + + curl_ = (*easy_init_)(); + + last_curl_error_ = "No Error"; + + if (!curl_) { + dlclose(curl_lib_); + LOG(WARNING) << "Curl initialization failed"; + return false; + } + + // Disable 100-continue header. + char buf[] = "Expect:"; + + headerlist_ = (*slist_append_)(headerlist_, buf); + (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_); + return true; +} + +#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name) \ + *(void**) (&var) = dlsym(curl_lib_, function_name); \ + if (!var) { \ + LOG(WARNING) << "Could not find libcurl function " << function_name; \ + init_ok_ = false; \ + return false; \ + } + +bool LibcurlWrapper::SetFunctionPointers() { + + SET_AND_CHECK_FUNCTION_POINTER(easy_init_, + "curl_easy_init"); + SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_, + "curl_easy_setopt"); + SET_AND_CHECK_FUNCTION_POINTER(formadd_, + "curl_formadd"); + SET_AND_CHECK_FUNCTION_POINTER(slist_append_, + "curl_slist_append"); + SET_AND_CHECK_FUNCTION_POINTER(easy_perform_, + "curl_easy_perform"); + SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_, + "curl_easy_cleanup"); + SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_, + "curl_slist_free_all"); + SET_AND_CHECK_FUNCTION_POINTER(formfree_, + "curl_formfree"); + return true; +} + +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h new file mode 100644 index 000000000000..08aa958640ce --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h @@ -0,0 +1,82 @@ +// Copyright (c) 2009, 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. + +// A wrapper for libcurl to do HTTP Uploads, to support easy mocking +// and unit testing of the HTTPUpload class. + +#include +#include +#include + +namespace google_breakpad { +class LibcurlWrapper { + public: + LibcurlWrapper(); + virtual bool Init(); + virtual bool SetProxy(const std::string& proxy_host, + const std::string& proxy_userpwd); + virtual bool AddFile(const std::string& upload_file_path, + const std::string& basename); + virtual bool SendRequest(const std::string& url, + const std::map& parameters, + std::string* server_response); + private: + // This function initializes class state corresponding to function + // pointers into the CURL library. + bool SetFunctionPointers(); + + bool init_ok_; // Whether init succeeded + void* curl_lib_; // Pointer to result of dlopen() on + // curl library + std::string last_curl_error_; // The text of the last error when + // dealing + // with CURL. + + CURL *curl_; // Pointer for handle for CURL calls. + + CURL* (*easy_init_)(void); + + // Stateful pointers for calling into curl_formadd() + struct curl_httppost *formpost_; + struct curl_httppost *lastptr_; + struct curl_slist *headerlist_; + + // Function pointers into CURL library + CURLcode (*easy_setopt_)(CURL *, CURLoption, ...); + CURLFORMcode (*formadd_)(struct curl_httppost **, + struct curl_httppost **, ...); + struct curl_slist* (*slist_append_)(struct curl_slist *, const char *); + void (*slist_free_all_)(struct curl_slist *); + CURLcode (*easy_perform_)(CURL *); + const char* (*easy_strerror_)(CURLcode); + void (*easy_cleanup_)(CURL *); + void (*formfree_)(struct curl_httppost *); + +}; +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h new file mode 100644 index 000000000000..e08f27f71356 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h @@ -0,0 +1,178 @@ +// Copyright (c) 2009, 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. + +// This header provides replacements for libc functions that we need. We if +// call the libc functions directly we risk crashing in the dynamic linker as +// it tries to resolve uncached PLT entries. + +#ifndef CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ +#define CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ + +#include +#include +#include + +extern "C" { + +static inline size_t +my_strlen(const char* s) { + size_t len = 0; + while (*s++) len++; + return len; +} + +static inline int +my_strcmp(const char* a, const char* b) { + for (;;) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } +} + +static inline int +my_strncmp(const char* a, const char* b, size_t len) { + for (size_t i = 0; i < len; ++i) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } + + return 0; +} + +// Parse a non-negative integer. +// result: (output) the resulting non-negative integer +// s: a NUL terminated string +// Return true iff successful. +static inline bool +my_strtoui(int* result, const char* s) { + if (*s == 0) + return false; + int r = 0; + for (;; s++) { + if (*s == 0) + break; + const int old_r = r; + r *= 10; + if (*s < '0' || *s > '9') + return false; + r += *s - '0'; + if (r < old_r) + return false; + } + + *result = r; + return true; +} + +// Return the length of the given, non-negative integer when expressed in base +// 10. +static inline unsigned +my_int_len(int i) { + if (!i) + return 1; + + int len = 0; + while (i) { + len++; + i /= 10; + } + + return len; +} + +// Convert a non-negative integer to a string +// output: (output) the resulting string is written here. This buffer must be +// large enough to hold the resulting string. Call |my_int_len| to get the +// required length. +// i: the non-negative integer to serialise. +// i_len: the length of the integer in base 10 (see |my_int_len|). +static inline void +my_itos(char* output, int i, unsigned i_len) { + for (unsigned index = i_len; index; --index, i /= 10) + output[index - 1] = '0' + (i % 10); +} + +static inline const char* +my_strchr(const char* haystack, char needle) { + while (*haystack && *haystack != needle) + haystack++; + if (*haystack == needle) + return haystack; + return (const char*) 0; +} + +// Read a hex value +// result: (output) the resulting value +// s: a string +// Returns a pointer to the first invalid charactor. +static inline const char* +my_read_hex_ptr(uintptr_t* result, const char* s) { + uintptr_t r = 0; + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { + r <<= 4; + r += *s - '0'; + } else if (*s >= 'a' && *s <= 'f') { + r <<= 4; + r += (*s - 'a') + 10; + } else if (*s >= 'A' && *s <= 'F') { + r <<= 4; + r += (*s - 'A') + 10; + } else { + break; + } + } + + *result = r; + return s; +} + +static inline void +my_memset(void* ip, char c, size_t len) { + char* p = (char *) ip; + while (len--) + *p++ = c; +} + +} // extern "C" + +#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc new file mode 100644 index 000000000000..d3907e947a7b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc @@ -0,0 +1,153 @@ +// Copyright (c) 2009, 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 "common/linux/linux_libc_support.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +typedef testing::Test LinuxLibcSupportTest; +} + +TEST(LinuxLibcSupportTest, strlen) { + static const char* test_data[] = { "", "a", "aa", "aaa", "aabc", NULL }; + for (unsigned i = 0; ; ++i) { + if (!test_data[i]) + break; + ASSERT_EQ(strlen(test_data[i]), my_strlen(test_data[i])); + } +} + +TEST(LinuxLibcSupportTest, strcmp) { + static const char* test_data[] = { + "", "", + "a", "", + "", "a", + "a", "b", + "a", "a", + "ab", "aa", + "abc", "ab", + "abc", "abc", + NULL, + }; + + for (unsigned i = 0; ; ++i) { + if (!test_data[i*2]) + break; + ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), + strcmp(test_data[i*2], test_data[i*2 + 1])); + } +} + +TEST(LinuxLibcSupportTest, strtoui) { + int result; + + ASSERT_FALSE(my_strtoui(&result, "")); + ASSERT_FALSE(my_strtoui(&result, "-1")); + ASSERT_FALSE(my_strtoui(&result, "-")); + ASSERT_FALSE(my_strtoui(&result, "a")); + ASSERT_FALSE(my_strtoui(&result, "23472893472938472987987398472398")); + + ASSERT_TRUE(my_strtoui(&result, "0")); + ASSERT_EQ(result, 0); + ASSERT_TRUE(my_strtoui(&result, "1")); + ASSERT_EQ(result, 1); + ASSERT_TRUE(my_strtoui(&result, "12")); + ASSERT_EQ(result, 12); + ASSERT_TRUE(my_strtoui(&result, "123")); + ASSERT_EQ(result, 123); + ASSERT_TRUE(my_strtoui(&result, "0123")); + ASSERT_EQ(result, 123); +} + +TEST(LinuxLibcSupportTest, int_len) { + ASSERT_EQ(my_int_len(0), 1); + ASSERT_EQ(my_int_len(2), 1); + ASSERT_EQ(my_int_len(5), 1); + ASSERT_EQ(my_int_len(9), 1); + ASSERT_EQ(my_int_len(10), 2); + ASSERT_EQ(my_int_len(99), 2); + ASSERT_EQ(my_int_len(100), 3); + ASSERT_EQ(my_int_len(101), 3); + ASSERT_EQ(my_int_len(1000), 4); +} + +TEST(LinuxLibcSupportTest, itos) { + char buf[10]; + + my_itos(buf, 0, 1); + ASSERT_EQ(0, memcmp(buf, "0", 1)); + + my_itos(buf, 1, 1); + ASSERT_EQ(0, memcmp(buf, "1", 1)); + + my_itos(buf, 10, 2); + ASSERT_EQ(0, memcmp(buf, "10", 2)); + + my_itos(buf, 63, 2); + ASSERT_EQ(0, memcmp(buf, "63", 2)); + + my_itos(buf, 101, 3); + ASSERT_EQ(0, memcmp(buf, "101", 2)); +} + +TEST(LinuxLibcSupportTest, strchr) { + ASSERT_EQ(NULL, my_strchr("abc", 'd')); + ASSERT_EQ(NULL, my_strchr("", 'd')); + ASSERT_EQ(NULL, my_strchr("efghi", 'd')); + + ASSERT_TRUE(my_strchr("a", 'a')); + ASSERT_TRUE(my_strchr("abc", 'a')); + ASSERT_TRUE(my_strchr("bcda", 'a')); + ASSERT_TRUE(my_strchr("sdfasdf", 'a')); +} + +TEST(LinuxLibcSupportTest, read_hex_ptr) { + uintptr_t result; + const char* last; + + last = my_read_hex_ptr(&result, ""); + ASSERT_EQ(result, 0); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0"); + ASSERT_EQ(result, 0); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123"); + ASSERT_EQ(result, 0x123); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a"); + ASSERT_EQ(result, 0x123a); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a-"); + ASSERT_EQ(result, 0x123a); + ASSERT_EQ(*last, '-'); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h new file mode 100644 index 000000000000..f95400dc169c --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h @@ -0,0 +1,2800 @@ +/* Copyright (c) 2005-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. + * + * --- + * Author: Markus Gutschke + */ + +/* This file includes Linux-specific support functions common to the + * coredumper and the thread lister; primarily, this is a collection + * of direct system calls, and a couple of symbols missing from + * standard header files. + * There are a few options that the including file can set to control + * the behavior of this file: + * + * SYS_CPLUSPLUS: + * The entire header file will normally be wrapped in 'extern "C" { }", + * making it suitable for compilation as both C and C++ source. If you + * do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit + * the wrapping. N.B. doing so will suppress inclusion of all prerequisite + * system header files, too. It is the caller's responsibility to provide + * the necessary definitions. + * + * SYS_ERRNO: + * All system calls will update "errno" unless overriden by setting the + * SYS_ERRNO macro prior to including this file. SYS_ERRNO should be + * an l-value. + * + * SYS_INLINE: + * New symbols will be defined "static inline", unless overridden by + * the SYS_INLINE macro. + * + * SYS_LINUX_SYSCALL_SUPPORT_H + * This macro is used to avoid multiple inclusions of this header file. + * If you need to include this file more than once, make sure to + * unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion. + * + * SYS_PREFIX: + * New system calls will have a prefix of "sys_" unless overridden by + * the SYS_PREFIX macro. Valid values for this macro are [0..9] which + * results in prefixes "sys[0..9]_". It is also possible to set this + * macro to -1, which avoids all prefixes. + * + * This file defines a few internal symbols that all start with "LSS_". + * Do not access these symbols from outside this file. They are not part + * of the supported API. + */ +#ifndef SYS_LINUX_SYSCALL_SUPPORT_H +#define SYS_LINUX_SYSCALL_SUPPORT_H + +/* We currently only support x86-32, x86-64, ARM, MIPS, and PPC on Linux. + * Porting to other related platforms should not be difficult. + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__mips__) || defined(__PPC__)) && defined(__linux) + +#ifndef SYS_CPLUSPLUS +#ifdef __cplusplus +/* Some system header files in older versions of gcc neglect to properly + * handle being included from C++. As it appears to be harmless to have + * multiple nested 'extern "C"' blocks, just add another one here. + */ +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __mips__ +/* Include definitions of the ABI currently in use. */ +#include +#endif + +#endif + +/* As glibc often provides subtly incompatible data structures (and implicit + * wrapper functions that convert them), we provide our own kernel data + * structures for use by the system calls. + * These structures have been developed by using Linux 2.6.23 headers for + * reference. Note though, we do not care about exact API compatibility + * with the kernel, and in fact the kernel often does not have a single + * API that works across architectures. Instead, we try to mimic the glibc + * API where reasonable, and only guarantee ABI compatibility with the + * kernel headers. + * Most notably, here are a few changes that were made to the structures + * defined by kernel headers: + * + * - we only define structures, but not symbolic names for kernel data + * types. For the latter, we directly use the native C datatype + * (i.e. "unsigned" instead of "mode_t"). + * - in a few cases, it is possible to define identical structures for + * both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by + * standardizing on the 64bit version of the data types. In particular, + * this means that we use "unsigned" where the 32bit headers say + * "unsigned long". + * - overall, we try to minimize the number of cases where we need to + * conditionally define different structures. + * - the "struct kernel_sigaction" class of structures have been + * modified to more closely mimic glibc's API by introducing an + * anonymous union for the function pointer. + * - a small number of field names had to have an underscore appended to + * them, because glibc defines a global macro by the same name. + */ + +/* include/linux/dirent.h */ +struct kernel_dirent64 { + unsigned long long d_ino; + long long d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +/* include/linux/dirent.h */ +struct kernel_dirent { + long d_ino; + long d_off; + unsigned short d_reclen; + char d_name[256]; +}; + +/* include/linux/uio.h */ +struct kernel_iovec { + void *iov_base; + unsigned long iov_len; +}; + +/* include/linux/socket.h */ +struct kernel_msghdr { + void *msg_name; + int msg_namelen; + struct kernel_iovec*msg_iov; + unsigned long msg_iovlen; + void *msg_control; + unsigned long msg_controllen; + unsigned msg_flags; +}; + +/* include/asm-generic/poll.h */ +struct kernel_pollfd { + int fd; + short events; + short revents; +}; + +/* include/linux/resource.h */ +struct kernel_rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; + +/* include/linux/time.h */ +struct kernel_timespec { + long tv_sec; + long tv_nsec; +}; + +/* include/linux/time.h */ +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; + +/* include/linux/resource.h */ +struct kernel_rusage { + struct kernel_timeval ru_utime; + struct kernel_timeval ru_stime; + long ru_maxrss; + long ru_ixrss; + long ru_idrss; + long ru_isrss; + long ru_minflt; + long ru_majflt; + long ru_nswap; + long ru_inblock; + long ru_oublock; + long ru_msgsnd; + long ru_msgrcv; + long ru_nsignals; + long ru_nvcsw; + long ru_nivcsw; +}; + +struct siginfo; +#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__) + +/* include/asm-{arm,i386,mips,ppc}/signal.h */ +struct kernel_old_sigaction { + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + unsigned long sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +} __attribute__((packed,aligned(4))); +#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define kernel_old_sigaction kernel_sigaction +#endif + +/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the + * exactly match the size of the signal set, even though the API was + * intended to be extensible. We define our own KERNEL_NSIG to deal with + * this. + * Please note that glibc provides signals [1.._NSIG-1], whereas the + * kernel (and this header) provides the range [1..KERNEL_NSIG]. The + * actual number of signals is obviously the same, but the constants + * differ by one. + */ +#ifdef __mips__ +#define KERNEL_NSIG 128 +#else +#define KERNEL_NSIG 64 +#endif + +/* include/asm-{arm,i386,mips,x86_64}/signal.h */ +struct kernel_sigset_t { + unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/ + (8*sizeof(unsigned long))]; +}; + +/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h */ +struct kernel_sigaction { +#ifdef __mips__ + unsigned long sa_flags; + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + struct kernel_sigset_t sa_mask; +#else + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + struct kernel_sigset_t sa_mask; +#endif +}; + +/* include/linux/socket.h */ +struct kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +/* include/asm-{arm,i386,mips,ppc}/stat.h */ +#ifdef __mips__ +#if _MIPS_SIM == _MIPS_SIM_ABI64 +struct kernel_stat { +#else +struct kernel_stat64 { +#endif + unsigned st_dev; + unsigned __pad0[3]; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + unsigned __pad1[3]; + long long st_size; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned st_blksize; + unsigned __pad2; + unsigned long long st_blocks; +}; +#elif defined __PPC__ +struct kernel_stat64 { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned short int __pad2; + long long st_size; + long st_blksize; + long long st_blocks; + long st_atime_; + unsigned long st_atime_nsec_; + long st_mtime_; + unsigned long st_mtime_nsec_; + long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#else +struct kernel_stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + unsigned __st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned char __pad3[4]; + long long st_size; + unsigned st_blksize; + unsigned long long st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned long long st_ino; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */ +#if defined(__i386__) || defined(__ARM_ARCH_3__) +struct kernel_stat { + /* The kernel headers suggest that st_dev and st_rdev should be 32bit + * quantities encoding 12bit major and 20bit minor numbers in an interleaved + * format. In reality, we do not see useful data in the top bits. So, + * we'll leave the padding in here, until we find a better solution. + */ + unsigned short st_dev; + short pad1; + unsigned st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + short pad2; + unsigned st_size; + unsigned st_blksize; + unsigned st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned __unused4; + unsigned __unused5; +}; +#elif defined(__x86_64__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned st_mode; + unsigned st_uid; + unsigned st_gid; + unsigned __pad0; + unsigned long st_rdev; + long st_size; + long st_blksize; + long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + long __unused[3]; +}; +#elif defined(__PPC__) +struct kernel_stat { + unsigned st_dev; + unsigned long st_ino; // ino_t + unsigned long st_mode; // mode_t + unsigned short st_nlink; // nlink_t + unsigned st_uid; // uid_t + unsigned st_gid; // gid_t + unsigned st_rdev; + long st_size; // off_t + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) +struct kernel_stat { + unsigned st_dev; + int st_pad1[3]; + unsigned st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + int st_pad2[2]; + long st_size; + int st_pad3; + long st_atime_; + long st_atime_nsec_; + long st_mtime_; + long st_mtime_nsec_; + long st_ctime_; + long st_ctime_nsec_; + int st_blksize; + int st_blocks; + int st_pad4[14]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc}/statfs.h */ +#ifdef __mips__ +#if _MIPS_SIM != _MIPS_SIM_ABI64 +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_frsize; + unsigned long __pad; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_files; + unsigned long long f_ffree; + unsigned long long f_bavail; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_spare[6]; +}; +#endif +#elif !defined(__x86_64__) +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc,generic}/statfs.h */ +#ifdef __mips__ +struct kernel_statfs { + long f_type; + long f_bsize; + long f_frsize; + long f_blocks; + long f_bfree; + long f_files; + long f_ffree; + long f_bavail; + struct { int val[2]; } f_fsid; + long f_namelen; + long f_spare[6]; +}; +#else +struct kernel_statfs { + /* x86_64 actually defines all these fields as signed, whereas all other */ + /* platforms define them as unsigned. Leaving them at unsigned should not */ + /* cause any problems. */ + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_blocks; + unsigned long f_bfree; + unsigned long f_bavail; + unsigned long f_files; + unsigned long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + + +/* Definitions missing from the standard header files */ +#ifndef O_DIRECTORY +#if defined(__ARM_ARCH_3__) +#define O_DIRECTORY 0040000 +#else +#define O_DIRECTORY 0200000 +#endif +#endif +#ifndef NT_PRXFPREG +#define NT_PRXFPREG 0x46e62b7f +#endif +#ifndef PTRACE_GETFPXREGS +#define PTRACE_GETFPXREGS ((enum __ptrace_request)18) +#endif +#ifndef PR_GET_DUMPABLE +#define PR_GET_DUMPABLE 3 +#endif +#ifndef PR_SET_DUMPABLE +#define PR_SET_DUMPABLE 4 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD (-100) +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_REMOVEDIR +#define AT_REMOVEDIR 0x200 +#endif +#ifndef MREMAP_FIXED +#define MREMAP_FIXED 2 +#endif +#ifndef SA_RESTORER +#define SA_RESTORER 0x04000000 +#endif + +#if defined(__i386__) +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_setresgid 170 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 208 +#define __NR_setresgid32 210 +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_gettid +#define __NR_gettid 224 +#endif +#ifndef __NR_readahead +#define __NR_readahead 225 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 226 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 227 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 229 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 230 +#endif +#ifndef __NR_futex +#define __NR_futex 240 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 258 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 268 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 269 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 272 +#endif +#ifndef __NR_openat +#define __NR_openat 295 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 300 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 301 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 317 +#endif +/* End of i386 definitions */ +#elif defined(__ARM_ARCH_3__) +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_SYSCALL_BASE + 164) +#define __NR_setresgid (__NR_SYSCALL_BASE + 170) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) +#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) +#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) +#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_SYSCALL_BASE + 180) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_SYSCALL_BASE + 195) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) +#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) +#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_SYSCALL_BASE + 224) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_SYSCALL_BASE + 225) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_SYSCALL_BASE + 226) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_SYSCALL_BASE + 229) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_SYSCALL_BASE + 240) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) +#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_SYSCALL_BASE + 344) +#endif +/* End of ARM 3 definitions */ +#elif defined(__x86_64__) +#ifndef __NR_setresuid +#define __NR_setresuid 117 +#define __NR_setresgid 119 +#endif +#ifndef __NR_gettid +#define __NR_gettid 186 +#endif +#ifndef __NR_readahead +#define __NR_readahead 187 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 188 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 189 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 191 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 192 +#endif +#ifndef __NR_futex +#define __NR_futex 202 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 203 +#define __NR_sched_getaffinity 204 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 217 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 218 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 221 +#endif +#ifndef __NR_openat +#define __NR_openat 257 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 262 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 263 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 279 +#endif +/* End of x86-64 definitions */ +#elif defined(__mips__) +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 185) +#define __NR_setresgid (__NR_Linux + 190) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction (__NR_Linux + 194) +#define __NR_rt_sigprocmask (__NR_Linux + 195) +#define __NR_rt_sigpending (__NR_Linux + 196) +#define __NR_rt_sigsuspend (__NR_Linux + 199) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 200) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 201) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_Linux + 213) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_Linux + 215) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_Linux + 219) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 222) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 223) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 224) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 225) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 227) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 228) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 238) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 239) +#define __NR_sched_getaffinity (__NR_Linux + 240) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 252) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 255) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 256) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 288) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 293) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 294) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 308) +#endif +/* End of MIPS (old 32bit API) definitions */ +#elif _MIPS_SIM == _MIPS_SIM_ABI64 +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_setresgid (__NR_Linux + 117) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 212) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 247) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 252) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 253) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 267) +#endif +/* End of MIPS (64bit API) definitions */ +#else +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_setresgid (__NR_Linux + 117) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 213) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 217) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 218) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 251) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 256) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 257) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 271) +#endif +/* End of MIPS (new 32bit API) definitions */ +#endif +/* End of MIPS definitions */ +#elif defined(__PPC__) +#ifndef __NR_setfsuid +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_setresgid 169 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 173 +#define __NR_rt_sigprocmask 174 +#define __NR_rt_sigpending 175 +#define __NR_rt_sigsuspend 178 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 179 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 180 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 190 +#endif +#ifndef __NR_readahead +#define __NR_readahead 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 202 +#endif +#ifndef __NR_gettid +#define __NR_gettid 207 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 209 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 210 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 212 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 213 +#endif +#ifndef __NR_futex +#define __NR_futex 221 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 222 +#define __NR_sched_getaffinity 223 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 232 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 252 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 253 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 254 +#endif +#ifndef __NR_openat +#define __NR_openat 286 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 291 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 292 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 301 +#endif +/* End of powerpc defininitions */ +#endif + + +/* After forking, we must make sure to only call system calls. */ +#if __BOUNDED_POINTERS__ + #error "Need to port invocations of syscalls for bounded ptrs" +#else + /* The core dumper and the thread lister get executed after threads + * have been suspended. As a consequence, we cannot call any functions + * that acquire locks. Unfortunately, libc wraps most system calls + * (e.g. in order to implement pthread_atfork, and to make calls + * cancellable), which means we cannot call these functions. Instead, + * we have to call syscall() directly. + */ + #undef LSS_ERRNO + #ifdef SYS_ERRNO + /* Allow the including file to override the location of errno. This can + * be useful when using clone() with the CLONE_VM option. + */ + #define LSS_ERRNO SYS_ERRNO + #else + #define LSS_ERRNO errno + #endif + + #undef LSS_INLINE + #ifdef SYS_INLINE + #define LSS_INLINE SYS_INLINE + #else + #define LSS_INLINE static inline + #endif + + /* Allow the including file to override the prefix used for all new + * system calls. By default, it will be set to "sys_". + */ + #undef LSS_NAME + #ifndef SYS_PREFIX + #define LSS_NAME(name) sys_##name + #elif SYS_PREFIX < 0 + #define LSS_NAME(name) name + #elif SYS_PREFIX == 0 + #define LSS_NAME(name) sys0_##name + #elif SYS_PREFIX == 1 + #define LSS_NAME(name) sys1_##name + #elif SYS_PREFIX == 2 + #define LSS_NAME(name) sys2_##name + #elif SYS_PREFIX == 3 + #define LSS_NAME(name) sys3_##name + #elif SYS_PREFIX == 4 + #define LSS_NAME(name) sys4_##name + #elif SYS_PREFIX == 5 + #define LSS_NAME(name) sys5_##name + #elif SYS_PREFIX == 6 + #define LSS_NAME(name) sys6_##name + #elif SYS_PREFIX == 7 + #define LSS_NAME(name) sys7_##name + #elif SYS_PREFIX == 8 + #define LSS_NAME(name) sys8_##name + #elif SYS_PREFIX == 9 + #define LSS_NAME(name) sys9_##name + #endif + + #undef LSS_RETURN + #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__)) + /* Failing system calls return a negative result in the range of + * -1..-4095. These are "errno" values with the sign inverted. + */ + #define LSS_RETURN(type, res) \ + do { \ + if ((unsigned long)(res) >= (unsigned long)(-4095)) { \ + LSS_ERRNO = -(res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__mips__) + /* On MIPS, failing system calls return -1, and set errno in a + * separate CPU register. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__PPC__) + /* On PPC, failing system calls return -1, and set errno in a + * separate CPU register. See linux/unistd.h. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err & 0x10000000 ) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #endif + #if defined(__i386__) + /* In PIC mode (e.g. when building shared libraries), gcc for i386 + * reserves ebx. Unfortunately, most distribution ship with implementations + * of _syscallX() which clobber ebx. + * Also, most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_BODY + #define LSS_BODY(type,args...) \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + "int $0x80\n" \ + "pop %%ebx" \ + args \ + : "memory"); \ + LSS_RETURN(type,__res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)(void) { \ + long __res; \ + __asm__ volatile("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1,type2 arg2) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4))); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + "movl %1,%%eax\n" \ + "int $0x80\n" \ + "pop %%ebx" \ + : "=a" (__res) \ + : "i" (__NR_##name), "ri" ((long)(arg1)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 }; \ + __asm__ __volatile__("push %%ebp\n" \ + "push %%ebx\n" \ + "movl 4(%2),%%ebp\n" \ + "movl 0(%2), %%ebx\n" \ + "movl %1,%%eax\n" \ + "int $0x80\n" \ + "pop %%ebx\n" \ + "pop %%ebp" \ + : "=a" (__res) \ + : "i" (__NR_##name), "0" ((long)(&__s)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "movl %3,%%ecx\n" + "jecxz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "movl %4,%%ecx\n" + "jecxz 1f\n" + + /* Set up alignment of the child stack: + * child_stack = (child_stack & ~0xF) - 20; + */ + "andl $-16,%%ecx\n" + "subl $20,%%ecx\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movl %6,%%eax\n" + "movl %%eax,4(%%ecx)\n" + "movl %3,%%eax\n" + "movl %%eax,(%%ecx)\n" + + /* %eax = syscall(%eax = __NR_clone, + * %ebx = flags, + * %ecx = child_stack, + * %edx = parent_tidptr, + * %esi = newtls, + * %edi = child_tidptr) + * Also, make sure that %ebx gets preserved as it is + * used in PIC mode. + */ + "movl %8,%%esi\n" + "movl %7,%%edx\n" + "movl %5,%%eax\n" + "movl %9,%%edi\n" + "pushl %%ebx\n" + "movl %%eax,%%ebx\n" + "movl %2,%%eax\n" + "int $0x80\n" + + /* In the parent: restore %ebx + * In the child: move "fn" into %ebx + */ + "popl %%ebx\n" + + /* if (%eax != 0) + * return %eax; + */ + "test %%eax,%%eax\n" + "jnz 1f\n" + + /* In the child, now. Terminate frame pointer chain. + */ + "movl $0,%%ebp\n" + + /* Call "fn". "arg" is already on the stack. + */ + "call *%%ebx\n" + + /* Call _exit(%ebx). Unfortunately older versions + * of gcc restrict the number of arguments that can + * be passed to asm(). So, we need to hard-code the + * system call number. + */ + "movl %%eax,%%ebx\n" + "movl $1,%%eax\n" + "int $0x80\n" + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), + "m"(fn), "m"(child_stack), "m"(flags), "m"(arg), + "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr) + : "memory", "ecx", "edx", "esi", "edi"); + LSS_RETURN(int, __res); + } + + #define __NR__fadvise64_64 __NR_fadvise64_64 + LSS_INLINE _syscall6(int, _fadvise64_64, int, fd, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi, + int, advice) + + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, + loff_t len, int advice) { + return LSS_NAME(_fadvise64_64)(fd, + (unsigned)offset, (unsigned)(offset >>32), + (unsigned)len, (unsigned)(len >> 32), + advice); + } + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movl %1,%%eax\n" + "int $0x80\n" + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + LSS_INLINE void (*LSS_NAME(restore)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:pop %%eax\n" + "movl %1,%%eax\n" + "int $0x80\n" + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_sigreturn)); + return res; + } + #elif defined(__x86_64__) + /* There are no known problems with any of the _syscallX() macros + * currently shipping for x86_64, but we still need to be able to define + * our own version so that we can override the location of the errno + * location (e.g. when using the clone() system call with the CLONE_VM + * option). + */ + #undef LSS_BODY + #define LSS_BODY(type,name, ...) \ + long __res; \ + __asm__ __volatile__("syscall" : "=a" (__res) : "0" (__NR_##name), \ + ##__VA_ARGS__ : "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, name, "D" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; syscall" : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "g" ((long)(arg4)) : "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; syscall" : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "g" ((long)(arg4)), "g" ((long)(arg5)) : \ + "r8", "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; movq %7,%%r9;" \ + "syscall" : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "g" ((long)(arg4)), "g" ((long)(arg5)), "g" ((long)(arg6)) : \ + "r8", "r9", "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register void *__tls __asm__("r8") = newtls; + register int *__ctid __asm__("r10") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "testq %4,%4\n" + "jz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "testq %5,%5\n" + "jz 1f\n" + + /* childstack -= 2*sizeof(void *); + */ + "subq $16,%5\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movq %7,8(%5)\n" + "movq %4,0(%5)\n" + + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "movq %2,%%rax\n" + "syscall\n" + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate frame pointer chain. + */ + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". + */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%ebx). + */ + "movq %%rax,%%rdi\n" + "movq %3,%%rax\n" + "syscall\n" + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(fn), "S"(child_stack), "D"(flags), "r"(arg), + "d"(parent_tidptr), "r"(__tls), "r"(__ctid) + : "memory", "r11", "rcx"); + } + LSS_RETURN(int, __res); + } + LSS_INLINE _syscall4(int, fadvise64, int, fd, loff_t, offset, loff_t, len, + int, advice) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On x86-64, the kernel does not know how to return from + * a signal handler. Instead, it relies on user space to provide a + * restorer function that calls the rt_sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movq %1,%%rax\n" + "syscall\n" + "2:popq %0\n" + "addq $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + #elif defined(__ARM_ARCH_3__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all fo the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ (__syscall(name) \ + : "=r"(__res_r0) : args : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "cmp %2,#0\n" + "cmpne %3,#0\n" + "moveq %0,%1\n" + "beq 1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "str %5,[%3,#-4]!\n" + "str %2,[%3,#-4]!\n" + + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + __syscall(clone)"\n" + + /* if (%r0 != 0) + * return %r0; + */ + "movs %0,r0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + "mov lr,pc\n" + "ldr pc,[sp]\n" + + /* Call _exit(%r0). + */ + __syscall(exit)"\n" + "1:\n" + : "=r" (__res) + : "i"(-EINVAL), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid) + : "lr", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__mips__) + #undef LSS_REG + #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \ + (unsigned long)(a) + #undef LSS_BODY + #define LSS_BODY(type,name,r7,...) \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ ("syscall\n" \ + : "=&r"(__v0), r7 (__r7) \ + : "0"(__v0), ##__VA_ARGS__ \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_BODY(type, name, "=r"); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall5 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2"); \ + __asm__ __volatile__ (".set noreorder\n" \ + "lw $2, %6\n" \ + "subu $29, 32\n" \ + "sw $2, 16($29)\n" \ + "li $2, %2\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "=&r"(__v0), "+r" (__r7) \ + : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ + "r"(__r6), "m" ((unsigned long)arg5) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8)); \ + } + #endif + #undef _syscall6 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2"); \ + __asm__ __volatile__ (".set noreorder\n" \ + "lw $2, %6\n" \ + "lw $8, %7\n" \ + "subu $29, 32\n" \ + "sw $2, 16($29)\n" \ + "sw $8, 20($29)\n" \ + "li $2, %2\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "=&r"(__v0), "+r" (__r7) \ + : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ + "r"(__r6), "r" ((unsigned long)arg5), \ + "r" ((unsigned long)arg6) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5,type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8), "r"(__r9)); \ + } + #endif + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + register unsigned long __v0 __asm__("$2"); + register unsigned long __r7 __asm__("$7") = (unsigned long)newtls; + { + register int __flags __asm__("$4") = flags; + register void *__stack __asm__("$5") = child_stack; + register void *__ptid __asm__("$6") = parent_tidptr; + register int *__ctid __asm__("$8") = child_tidptr; + __asm__ __volatile__( + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu $29,24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub $29,16\n" + #else + "dsubu $29,16\n" + #endif + + /* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "li %0,%2\n" + "beqz %5,1f\n" + "beqz %6,1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu %6,32\n" + "sw %5,0(%6)\n" + "sw %8,4(%6)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub %6,32\n" + "sw %5,0(%6)\n" + "sw %8,8(%6)\n" + #else + "dsubu %6,32\n" + "sd %5,0(%6)\n" + "sd %8,8(%6)\n" + #endif + + /* $7 = syscall($4 = flags, + * $5 = child_stack, + * $6 = parent_tidptr, + * $7 = newtls, + * $8 = child_tidptr) + */ + "li $2,%3\n" + "syscall\n" + + /* if ($7 != 0) + * return $2; + */ + "bnez $7,1f\n" + "bnez $2,1f\n" + + /* In the child, now. Call "fn(arg)". + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "lw $25,0($29)\n" + "lw $4,4($29)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "lw $25,0($29)\n" + "lw $4,8($29)\n" + #else + "ld $25,0($29)\n" + "ld $4,8($29)\n" + #endif + "jalr $25\n" + + /* Call _exit($2) + */ + "move $4,$2\n" + "li $2,%4\n" + "syscall\n" + + "1:\n" + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "addu $29, 24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "add $29, 16\n" + #else + "daddu $29,16\n" + #endif + : "=&r" (__v0), "=r" (__r7) + : "i"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__r7), "r"(__ctid) + : "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$24", "memory"); + } + LSS_RETURN(int, __v0, __r7); + } + #elif defined (__PPC__) + #undef LSS_LOADARGS_0 + #define LSS_LOADARGS_0(name, dummy...) \ + __sc_0 = __NR_##name + #undef LSS_LOADARGS_1 + #define LSS_LOADARGS_1(name, arg1) \ + LSS_LOADARGS_0(name); \ + __sc_3 = (unsigned long) (arg1) + #undef LSS_LOADARGS_2 + #define LSS_LOADARGS_2(name, arg1, arg2) \ + LSS_LOADARGS_1(name, arg1); \ + __sc_4 = (unsigned long) (arg2) + #undef LSS_LOADARGS_3 + #define LSS_LOADARGS_3(name, arg1, arg2, arg3) \ + LSS_LOADARGS_2(name, arg1, arg2); \ + __sc_5 = (unsigned long) (arg3) + #undef LSS_LOADARGS_4 + #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4) \ + LSS_LOADARGS_3(name, arg1, arg2, arg3); \ + __sc_6 = (unsigned long) (arg4) + #undef LSS_LOADARGS_5 + #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5) \ + LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4); \ + __sc_7 = (unsigned long) (arg5) + #undef LSS_LOADARGS_6 + #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6) \ + LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5); \ + __sc_8 = (unsigned long) (arg6) + #undef LSS_ASMINPUT_0 + #define LSS_ASMINPUT_0 "0" (__sc_0) + #undef LSS_ASMINPUT_1 + #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3) + #undef LSS_ASMINPUT_2 + #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4) + #undef LSS_ASMINPUT_3 + #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5) + #undef LSS_ASMINPUT_4 + #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6) + #undef LSS_ASMINPUT_5 + #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7) + #undef LSS_ASMINPUT_6 + #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8) + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + \ + LSS_LOADARGS_##nr(name, args); \ + __asm__ __volatile__ \ + ("sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory", \ + "r9", "r10", "r11", "r12"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, arg1); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, arg1, arg2); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, arg1, arg2, arg3); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, arg1, arg2, arg3, arg4); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \ + } + /* clone function adapted from glibc 2.3.6 clone.S */ + /* TODO(csilvers): consider wrapping some args up in a struct, like we + * do for i386's _syscall6, so we can compile successfully on gcc 2.95 + */ + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret, __err; + { + register int (*__fn)(void *) __asm__ ("r8") = fn; + register void *__cstack __asm__ ("r4") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void * __arg __asm__ ("r9") = arg; + register int * __ptidptr __asm__ ("r5") = parent_tidptr; + register void * __newtls __asm__ ("r6") = newtls; + register int * __ctidptr __asm__ ("r7") = child_tidptr; + __asm__ __volatile__( + /* check for fn == NULL + * and child_stack == NULL + */ + "cmpwi cr0, %6, 0\n\t" + "cmpwi cr1, %7, 0\n\t" + "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t" + "beq- cr0, 1f\n\t" + + /* set up stack frame for child */ + "clrrwi %7, %7, 4\n\t" + "li 0, 0\n\t" + "stwu 0, -16(%7)\n\t" + + /* fn, arg, child_stack are saved across the syscall: r28-30 */ + "mr 28, %6\n\t" + "mr 29, %7\n\t" + "mr 27, %9\n\t" + + /* syscall */ + "li 0, %4\n\t" + /* flags already in r3 + * child_stack already in r4 + * ptidptr already in r5 + * newtls already in r6 + * ctidptr already in r7 + */ + "sc\n\t" + + /* Test if syscall was successful */ + "cmpwi cr1, 3, 0\n\t" + "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" + "bne- cr1, 1f\n\t" + + /* Do the function call */ + "mtctr 28\n\t" + "mr 3, 27\n\t" + "bctrl\n\t" + + /* Call _exit(r3) */ + "li 0, %5\n\t" + "sc\n\t" + + /* Return to parent */ + "1:\n" + "mfcr %1\n\t" + "mr %0, 3\n\t" + : "=r" (__ret), "=r" (__err) + : "0" (-1), "1" (EINVAL), + "i" (__NR_clone), "i" (__NR_exit), + "r" (__fn), "r" (__cstack), "r" (__flags), + "r" (__arg), "r" (__ptidptr), "r" (__newtls), + "r" (__ctidptr) + : "cr0", "cr1", "memory", "ctr", + "r0", "r29", "r27", "r28"); + } + LSS_RETURN(int, __ret, __err); + } + #endif + #define __NR__exit __NR_exit + #define __NR__gettid __NR_gettid + #define __NR__mremap __NR_mremap + LSS_INLINE _syscall1(int, chdir, const char *,p) + LSS_INLINE _syscall1(int, close, int, f) + LSS_INLINE _syscall1(int, dup, int, f) + LSS_INLINE _syscall2(int, dup2, int, s, + int, d) + LSS_INLINE _syscall3(int, execve, const char*, f, + const char*const*,a,const char*const*, e) + LSS_INLINE _syscall1(int, _exit, int, e) + LSS_INLINE _syscall3(int, fcntl, int, f, + int, c, long, a) + LSS_INLINE _syscall0(pid_t, fork) + LSS_INLINE _syscall2(int, fstat, int, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, fstatfs, int, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall4(int, futex, int*, a, + int, o, int, v, + struct kernel_timespec*, t) + LSS_INLINE _syscall3(int, getdents, int, f, + struct kernel_dirent*, d, int, c) + LSS_INLINE _syscall3(int, getdents64, int, f, + struct kernel_dirent64*, d, int, c) + LSS_INLINE _syscall0(gid_t, getegid) + LSS_INLINE _syscall0(uid_t, geteuid) + LSS_INLINE _syscall0(pid_t, getpgrp) + LSS_INLINE _syscall0(pid_t, getpid) + LSS_INLINE _syscall0(pid_t, getppid) + LSS_INLINE _syscall2(int, getpriority, int, a, + int, b) + LSS_INLINE _syscall2(int, getrlimit, int, r, + struct kernel_rlimit*, l) + LSS_INLINE _syscall1(pid_t, getsid, pid_t, p) + LSS_INLINE _syscall0(pid_t, _gettid) + LSS_INLINE _syscall5(int, setxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall5(int, lsetxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall4(ssize_t, getxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall2(int, kill, pid_t, p, + int, s) + LSS_INLINE _syscall3(off_t, lseek, int, f, + off_t, o, int, w) + LSS_INLINE _syscall2(int, munmap, void*, s, + size_t, l) + LSS_INLINE _syscall6(long, move_pages, pid_t, p, + unsigned long, n, void **,g, int *, d, + int *, s, int, f) + LSS_INLINE _syscall5(void*, _mremap, void*, o, + size_t, os, size_t, ns, + unsigned long, f, void *, a) + LSS_INLINE _syscall3(int, open, const char*, p, + int, f, int, m) + LSS_INLINE _syscall3(int, poll, struct kernel_pollfd*, u, + unsigned int, n, int, t) + LSS_INLINE _syscall2(int, prctl, int, o, + long, a) + LSS_INLINE _syscall4(long, ptrace, int, r, + pid_t, p, void *, a, void *, d) + LSS_INLINE _syscall3(ssize_t, read, int, f, + void *, b, size_t, c) + LSS_INLINE _syscall3(int, readlink, const char*, p, + char*, b, size_t, s) + LSS_INLINE _syscall4(int, rt_sigaction, int, s, + const struct kernel_sigaction*, a, + struct kernel_sigaction*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, + size_t, c) + LSS_INLINE _syscall4(int, rt_sigprocmask, int, h, + const struct kernel_sigset_t*, s, + struct kernel_sigset_t*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigsuspend, + const struct kernel_sigset_t*, s, size_t, c) + LSS_INLINE _syscall3(int, sched_getaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall3(int, sched_setaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall0(int, sched_yield) + LSS_INLINE _syscall1(long, set_tid_address, int *, t) + LSS_INLINE _syscall1(int, setfsgid, gid_t, g) + LSS_INLINE _syscall1(int, setfsuid, uid_t, u) + LSS_INLINE _syscall2(int, setpgid, pid_t, p, + pid_t, g) + LSS_INLINE _syscall3(int, setpriority, int, a, + int, b, int, p) + LSS_INLINE _syscall3(int, setresgid, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, setresuid, uid_t, r, + uid_t, e, uid_t, s) + LSS_INLINE _syscall2(int, setrlimit, int, r, + const struct kernel_rlimit*, l) + LSS_INLINE _syscall0(pid_t, setsid) + LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s, + const stack_t*, o) + LSS_INLINE _syscall2(int, stat, const char*, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, statfs, const char*, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall1(int, unlink, const char*, f) + LSS_INLINE _syscall3(ssize_t, write, int, f, + const void *, b, size_t, c) + LSS_INLINE _syscall3(ssize_t, writev, int, f, + const struct kernel_iovec*, v, size_t, c) + #if defined(__x86_64__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(int, recvmsg, int, s, + struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall3(int, sendmsg, int, s, + const struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall6(int, sendto, int, s, + const void*, m, size_t, l, + int, f, + const struct kernel_sockaddr*, a, int, t) + LSS_INLINE _syscall2(int, shutdown, int, s, + int, h) + LSS_INLINE _syscall3(int, socket, int, d, + int, t, int, p) + LSS_INLINE _syscall4(int, socketpair, int, d, + int, t, int, p, int*, s) + #endif + #if defined(__x86_64__) + LSS_INLINE _syscall6(void*, mmap, void*, s, + size_t, l, int, p, + int, f, int, d, + __off64_t, o) + LSS_INLINE _syscall4(int, newfstatat, int, d, + const char *, p, + struct kernel_stat*, b, int, f) + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + return LSS_NAME(setfsgid)(gid); + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + return LSS_NAME(setfsuid)(uid); + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + return LSS_NAME(setresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + return LSS_NAME(setresuid)(ruid, euid, suid); + } + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + /* On x86_64, the kernel requires us to always set our own + * SA_RESTORER in order to be able to return from a signal handler. + * This function must have a "magic" signature that the "gdb" + * (and maybe the kernel?) can recognize. + */ + if (act != NULL && !(act->sa_flags & SA_RESTORER)) { + struct kernel_sigaction a = *act; + a.sa_flags |= SA_RESTORER; + a.sa_restorer = LSS_NAME(restore_rt)(); + return LSS_NAME(rt_sigaction)(signum, &a, oldact, + (KERNEL_NSIG+7)/8); + } else { + return LSS_NAME(rt_sigaction)(signum, act, oldact, + (KERNEL_NSIG+7)/8); + } + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall4(pid_t, wait4, pid_t, p, + int*, s, int, o, + struct kernel_rusage*, r) + + LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options){ + return LSS_NAME(wait4)(pid, status, options, 0); + } + #endif + #if defined(__i386__) || defined(__x86_64__) + LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m) + LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) + #define __NR__setfsgid32 __NR_setfsgid32 + #define __NR__setfsuid32 __NR_setfsuid32 + #define __NR__setresgid32 __NR_setresgid32 + #define __NR__setresuid32 __NR_setresuid32 + LSS_INLINE _syscall2(int, ugetrlimit, int, r, + struct kernel_rlimit*, l) + LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f) + LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f) + LSS_INLINE _syscall3(int, _setresgid32, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, _setresuid32, uid_t, r, + uid_t, e, uid_t, s) + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + int rc; + if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)gid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsgid)(gid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + int rc; + if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)uid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsuid)(uid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + int rc; + if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)rgid & ~0xFFFFu || + (unsigned int)egid & ~0xFFFFu || + (unsigned int)sgid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresgid)(rgid, egid, sgid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + int rc; + if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)ruid & ~0xFFFFu || + (unsigned int)euid & ~0xFFFFu || + (unsigned int)suid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresuid)(ruid, euid, suid); + } + } + return rc; + } + #endif + LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) { + memset(&set->sig, 0, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) { + memset(&set->sig, -1, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0]))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0])))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] & + (1UL << ((signum - 1) % (8*sizeof(set->sig[0]))))); + } + } + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__) + #define __NR__sigaction __NR_sigaction + #define __NR__sigpending __NR_sigpending + #define __NR__sigprocmask __NR_sigprocmask + #define __NR__sigsuspend __NR_sigsuspend + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, fstat64, int, f, + struct kernel_stat64 *, b) + LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, + loff_t *, res, uint, wh) + LSS_INLINE _syscall1(void*, mmap, void*, a) + LSS_INLINE _syscall6(void*, mmap2, void*, s, + size_t, l, int, p, + int, f, int, d, + __off64_t, o) + LSS_INLINE _syscall3(int, _sigaction, int, s, + const struct kernel_old_sigaction*, a, + struct kernel_old_sigaction*, o) + LSS_INLINE _syscall1(int, _sigpending, unsigned long*, s) + LSS_INLINE _syscall3(int, _sigprocmask, int, h, + const unsigned long*, s, + unsigned long*, o) + #ifdef __PPC__ + LSS_INLINE _syscall1(int, _sigsuspend, unsigned long, s) + #else + LSS_INLINE _syscall3(int, _sigsuspend, const void*, a, + int, b, + unsigned long, s) + #endif + LSS_INLINE _syscall2(int, stat64, const char *, p, + struct kernel_stat64 *, b) + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + int old_errno = LSS_ERRNO; + int rc; + struct kernel_sigaction a; + if (act != NULL) { + a = *act; + #ifdef __i386__ + /* On i386, the kernel requires us to always set our own + * SA_RESTORER when using realtime signals. Otherwise, it does not + * know how to return from a signal handler. This function must have + * a "magic" signature that the "gdb" (and maybe the kernel?) can + * recognize. + * Apparently, a SA_RESTORER is implicitly set by the kernel, when + * using non-realtime signals. + * + * TODO: Test whether ARM needs a restorer + */ + if (!(a.sa_flags & SA_RESTORER)) { + a.sa_flags |= SA_RESTORER; + a.sa_restorer = (a.sa_flags & SA_SIGINFO) + ? LSS_NAME(restore_rt)() : LSS_NAME(restore)(); + } + #endif + } + rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact, + (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa; + if (!act) { + ptr_a = NULL; + } else { + oa.sa_handler_ = act->sa_handler_; + memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask)); + #ifndef __mips__ + oa.sa_restorer = act->sa_restorer; + #endif + oa.sa_flags = act->sa_flags; + } + if (!oldact) { + ptr_oa = NULL; + } + LSS_ERRNO = old_errno; + rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa); + if (rc == 0 && oldact) { + if (act) { + memcpy(oldact, act, sizeof(*act)); + } else { + memset(oldact, 0, sizeof(*oldact)); + } + oldact->sa_handler_ = ptr_oa->sa_handler_; + oldact->sa_flags = ptr_oa->sa_flags; + memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask)); + #ifndef __mips__ + oldact->sa_restorer = ptr_oa->sa_restorer; + #endif + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + int old_errno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = old_errno; + LSS_NAME(sigemptyset)(set); + rc = LSS_NAME(_sigpending)(&set->sig[0]); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + if (oldset) { + LSS_NAME(sigemptyset)(oldset); + } + rc = LSS_NAME(_sigprocmask)(how, + set ? &set->sig[0] : NULL, + oldset ? &oldset->sig[0] : NULL); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + rc = LSS_NAME(_sigsuspend)( + #ifndef __PPC__ + set, 0, + #endif + set->sig[0]); + } + return rc; + } + #endif + #if defined(__PPC__) + #undef LSS_SC_LOADARGS_0 + #define LSS_SC_LOADARGS_0(dummy...) + #undef LSS_SC_LOADARGS_1 + #define LSS_SC_LOADARGS_1(arg1) \ + __sc_4 = (unsigned long) (arg1) + #undef LSS_SC_LOADARGS_2 + #define LSS_SC_LOADARGS_2(arg1, arg2) \ + LSS_SC_LOADARGS_1(arg1); \ + __sc_5 = (unsigned long) (arg2) + #undef LSS_SC_LOADARGS_3 + #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \ + LSS_SC_LOADARGS_2(arg1, arg2); \ + __sc_6 = (unsigned long) (arg3) + #undef LSS_SC_LOADARGS_4 + #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \ + LSS_SC_LOADARGS_3(arg1, arg2, arg3); \ + __sc_7 = (unsigned long) (arg4) + #undef LSS_SC_LOADARGS_5 + #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \ + LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \ + __sc_8 = (unsigned long) (arg5) + #undef LSS_SC_BODY + #define LSS_SC_BODY(nr, type, opt, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \ + register unsigned long __sc_3 __asm__ ("r3") = opt; \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + LSS_SC_LOADARGS_##nr(args); \ + __asm__ __volatile__ \ + ("stwu 1, -48(1)\n\t" \ + "stw 4, 20(1)\n\t" \ + "stw 5, 24(1)\n\t" \ + "stw 6, 28(1)\n\t" \ + "stw 7, 32(1)\n\t" \ + "stw 8, 36(1)\n\t" \ + "addi 4, 1, 20\n\t" \ + "sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + LSS_SC_BODY(3, ssize_t, 17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + LSS_SC_BODY(3, ssize_t, 16, s, msg, flags); + } + + // TODO(csilvers): why is this ifdef'ed out? +#if 0 + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen); + } +#endif + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + LSS_SC_BODY(2, int, 13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + LSS_SC_BODY(3, int, 1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + LSS_SC_BODY(4, int, 8, d, type, protocol, sv); + } + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, _socketcall, int, c, + va_list, a) + + LSS_INLINE int LSS_NAME(socketcall)(int op, ...) { + int rc; + va_list ap; + va_start(ap, op); + rc = LSS_NAME(_socketcall)(op, ap); + va_end(ap); + return rc; + } + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen); + } + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + return LSS_NAME(socketcall)(13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + return LSS_NAME(socketcall)(1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + return LSS_NAME(socketcall)(8, d, type, protocol, sv); + } + #endif + #if defined(__i386__) || defined(__PPC__) + LSS_INLINE _syscall4(int, fstatat64, int, d, + const char *, p, + struct kernel_stat64 *, b, int, f) + #endif + #if defined(__i386__) || defined(__PPC__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(pid_t, waitpid, pid_t, p, + int*, s, int, o) + #endif + #if defined(__mips__) + /* sys_pipe() on MIPS has non-standard calling conventions, as it returns + * both file handles through CPU registers. + */ + LSS_INLINE int LSS_NAME(pipe)(int *p) { + register unsigned long __v0 __asm__("$2") = __NR_pipe; + register unsigned long __v1 __asm__("$3"); + register unsigned long __r7 __asm__("$7"); + __asm__ __volatile__ ("syscall\n" + : "=&r"(__v0), "=&r"(__v1), "+r" (__r7) + : "0"(__v0) + : "$8", "$9", "$10", "$11", "$12", + "$13", "$14", "$15", "$24", "memory"); + if (__r7) { + LSS_ERRNO = __v0; + return -1; + } else { + p[0] = __v0; + p[1] = __v1; + return 0; + } + } + #else + LSS_INLINE _syscall1(int, pipe, int *, p) + #endif + /* TODO(csilvers): see if ppc can/should support this as well */ + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) + #define __NR__statfs64 __NR_statfs64 + #define __NR__fstatfs64 __NR_fstatfs64 + LSS_INLINE _syscall3(int, _statfs64, const char*, p, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE _syscall3(int, _fstatfs64, int, f, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE int LSS_NAME(statfs64)(const char *p, + struct kernel_statfs64 *b) { + return LSS_NAME(_statfs64)(p, sizeof(*b), b); + } + LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) { + return LSS_NAME(_fstatfs64)(f, sizeof(*b), b); + } + #endif + + LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) { + extern char **environ; + return LSS_NAME(execve)(path, argv, (const char *const *)environ); + } + + LSS_INLINE pid_t LSS_NAME(gettid)() { + pid_t tid = LSS_NAME(_gettid)(); + if (tid != -1) { + return tid; + } + return LSS_NAME(getpid)(); + } + + LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size, + size_t new_size, int flags, ...) { + va_list ap; + void *new_address, *rc; + va_start(ap, flags); + new_address = va_arg(ap, void *); + rc = LSS_NAME(_mremap)(old_address, old_size, new_size, + flags, new_address); + va_end(ap); + return rc; + } + + LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) { + /* PTRACE_DETACH can sometimes forget to wake up the tracee and it + * then sends job control signals to the real parent, rather than to + * the tracer. We reduce the risk of this happening by starting a + * whole new time slice, and then quickly sending a SIGCONT signal + * right after detaching from the tracee. + */ + int rc, err; + LSS_NAME(sched_yield)(); + rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0); + err = LSS_ERRNO; + LSS_NAME(kill)(pid, SIGCONT); + LSS_ERRNO = err; + return rc; + } + + LSS_INLINE int LSS_NAME(raise)(int sig) { + return LSS_NAME(kill)(LSS_NAME(getpid)(), sig); + } + + LSS_INLINE int LSS_NAME(setpgrp)() { + return LSS_NAME(setpgid)(0, 0); + } + + LSS_INLINE int LSS_NAME(sysconf)(int name) { + extern int __getpagesize(void); + switch (name) { + case _SC_OPEN_MAX: { + struct kernel_rlimit limit; + return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0 + ? 8192 : limit.rlim_cur; + } + case _SC_PAGESIZE: + return __getpagesize(); + default: + errno = ENOSYS; + return -1; + } + } + #if defined(__x86_64__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64) + /* pread64() and pwrite64() do not exist on 64-bit systems... */ + LSS_INLINE _syscall3(int, readahead, int, f, + loff_t, o, unsigned, c) + #else + #define __NR__pread64 __NR_pread64 + #define __NR__pwrite64 __NR_pwrite64 + #define __NR__readahead __NR_readahead + LSS_INLINE _syscall5(ssize_t, _pread64, int, f, + void *, b, size_t, c, unsigned, o1, + unsigned, o2) + LSS_INLINE _syscall5(ssize_t, _pwrite64, int, f, + const void *, b, size_t, c, unsigned, o1, + long, o2) + LSS_INLINE _syscall4(int, _readahead, int, f, + unsigned, o1, unsigned, o2, size_t, c) + /* We force 64bit-wide parameters onto the stack, then access each + * 32-bit component individually. This guarantees that we build the + * correct parameters independent of the native byte-order of the + * underlying architecture. + */ + LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count, + loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pread64)(fd, buf, count, o.arg[0], o.arg[1]); + } + LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf, + size_t count, loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pwrite64)(fd, buf, count, o.arg[0], o.arg[1]); + } + LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_readahead)(fd, o.arg[0], o.arg[1], len); + } + #endif +#endif + +#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS) +} +#endif + +#endif +#endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h b/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h new file mode 100644 index 000000000000..f10a194b47a0 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h @@ -0,0 +1,181 @@ +// Copyright (c) 2009, 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. + +#ifndef CLIENT_LINUX_HANDLER_MEMORY_H_ +#define CLIENT_LINUX_HANDLER_MEMORY_H_ + +#include +#include +#include +#include + +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +// This is very simple allocator which fetches pages from the kernel directly. +// Thus, it can be used even when the heap may be corrupted. +// +// There is no free operation. The pages are only freed when the object is +// destroyed. +class PageAllocator { + public: + PageAllocator() + : page_size_(getpagesize()), + last_(NULL), + current_page_(NULL), + page_offset_(0) { + } + + ~PageAllocator() { + FreeAll(); + } + + void *Alloc(unsigned bytes) { + if (!bytes) + return NULL; + + if (current_page_ && page_size_ - page_offset_ >= bytes) { + uint8_t *const ret = current_page_ + page_offset_; + page_offset_ += bytes; + if (page_offset_ == page_size_) { + page_offset_ = 0; + current_page_ = NULL; + } + + return ret; + } + + const unsigned pages = + (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_; + uint8_t *const ret = GetNPages(pages); + if (!ret) + return NULL; + + page_offset_ = (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % page_size_; + current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL; + + return ret + sizeof(PageHeader); + } + + private: + uint8_t *GetNPages(unsigned num_pages) { +#ifdef __x86_64 + void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#else + void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (a == MAP_FAILED) + return NULL; + + struct PageHeader *header = reinterpret_cast(a); + header->next = last_; + header->num_pages = num_pages; + last_ = header; + + return reinterpret_cast(a); + } + + void FreeAll() { + PageHeader *next; + + for (PageHeader *cur = last_; cur; cur = next) { + next = cur->next; + sys_munmap(cur, cur->num_pages * page_size_); + } + } + + struct PageHeader { + PageHeader *next; // pointer to the start of the next set of pages. + unsigned num_pages; // the number of pages in this set. + }; + + const unsigned page_size_; + PageHeader *last_; + uint8_t *current_page_; + unsigned page_offset_; +}; + +// A wasteful vector is like a normal std::vector, except that it's very much +// simplier and it allocates memory from a PageAllocator. It's wasteful +// because, when resizing, it always allocates a whole new array since the +// PageAllocator doesn't support realloc. +template +class wasteful_vector { + public: + wasteful_vector(PageAllocator *allocator, unsigned size_hint = 16) + : allocator_(allocator), + a_((T*) allocator->Alloc(sizeof(T) * size_hint)), + allocated_(size_hint), + used_(0) { + } + + void push_back(const T& new_element) { + if (used_ == allocated_) + Realloc(allocated_ * 2); + a_[used_++] = new_element; + } + + size_t size() const { + return used_; + } + + T& operator[](size_t index) { + return a_[index]; + } + + const T& operator[](size_t index) const { + return a_[index]; + } + + private: + void Realloc(unsigned new_size) { + T *new_array = + reinterpret_cast(allocator_->Alloc(sizeof(T) * new_size)); + memcpy(new_array, a_, used_ * sizeof(T)); + a_ = new_array; + allocated_ = new_size; + } + + PageAllocator *const allocator_; + T *a_; // pointer to an array of |allocated_| elements. + unsigned allocated_; // size of |a_|, in elements. + unsigned used_; // number of used slots in |a_|. +}; + +} // namespace google_breakpad + +inline void* operator new(size_t nbytes, + google_breakpad::PageAllocator& allocator) { + return allocator.Alloc(nbytes); +} + +#endif // CLIENT_LINUX_HANDLER_MEMORY_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc new file mode 100644 index 000000000000..66c83465a1eb --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2009, 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 "common/linux/memory.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test PageAllocatorTest; +} + +TEST(PageAllocatorTest, Setup) { + PageAllocator allocator; +} + +TEST(PageAllocatorTest, SmallObjects) { + PageAllocator allocator; + + for (unsigned i = 1; i < 1024; ++i) { + uint8_t *p = reinterpret_cast(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +TEST(PageAllocatorTest, LargeObject) { + PageAllocator allocator; + + uint8_t *p = reinterpret_cast(allocator.Alloc(10000)); + ASSERT_FALSE(p == NULL); + for (unsigned i = 1; i < 10; ++i) { + uint8_t *p = reinterpret_cast(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +namespace { +typedef testing::Test WastefulVectorTest; +} + +TEST(WastefulVectorTest, Setup) { + PageAllocator allocator_; + wasteful_vector v(&allocator_); + ASSERT_EQ(v.size(), 0u); +} + +TEST(WastefulVectorTest, Simple) { + PageAllocator allocator_; + wasteful_vector v(&allocator_); + + for (unsigned i = 0; i < 256; ++i) + v.push_back(i); + ASSERT_EQ(v.size(), 256u); + for (unsigned i = 0; i < 256; ++i) + ASSERT_EQ(v[i], i); +} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc new file mode 100644 index 000000000000..69bec9cd54b2 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc @@ -0,0 +1,167 @@ +// Copyright (c) 2009, 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 +#include +#include "common/linux/module.h" + +namespace google_breakpad { + +Module::Module(const string &name, const string &os, + const string &architecture, const string &id) : + name_(name), + os_(os), + architecture_(architecture), + id_(id), + load_address_(0) { } + +Module::~Module() { + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++) + delete it->second; + for (vector::iterator it = functions_.begin(); + it != functions_.end(); it++) + delete *it; +} + +void Module::SetLoadAddress(Address address) { + load_address_ = address; +} + +void Module::AddFunction(Function *function) { + functions_.push_back(function); +} + +void Module::AddFunctions(vector::iterator begin, + vector::iterator end) { + functions_.insert(functions_.end(), begin, end); +} + +Module::File *Module::FindFile(const string &name) { + // A tricky bit here. The key of each map entry needs to be a + // pointer to the entry's File's name string. This means that we + // can't do the initial lookup with any operation that would create + // an empty entry for us if the name isn't found (like, say, + // operator[] or insert do), because such a created entry's key will + // be a pointer the string passed as our argument. Since the key of + // a map's value type is const, we can't fix it up once we've + // created our file. lower_bound does the lookup without doing an + // insertion, and returns a good hint iterator to pass to insert. + // Our "destiny" is where we belong, whether we're there or not now. + FileByNameMap::iterator destiny = files_.lower_bound(&name); + if (destiny == files_.end() + || *destiny->first != name) { // Repeated string comparison, boo hoo. + File *file = new File; + file->name_ = name; + file->source_id_ = -1; + destiny = files_.insert(destiny, + FileByNameMap::value_type(&file->name_, file)); + } + return destiny->second; +} + +Module::File *Module::FindFile(const char *name) { + string name_string = name; + return FindFile(name_string); +} + +void Module::AssignSourceIds() { + // First, give every source file an id of -1. + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); file_it++) + file_it->second->source_id_ = -1; + + // Next, mark all files actually cited by our functions' line number + // info, by setting each one's source id to zero. + for (vector::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); func_it++) { + Function *func = *func_it; + for (vector::iterator line_it = func->lines_.begin(); + line_it != func->lines_.end(); line_it++) + line_it->file_->source_id_ = 0; + } + + // Finally, assign source ids to those files that have been marked. + // We could have just assigned source id numbers while traversing + // the line numbers, but doing it this way numbers the files in + // lexicographical order by name, which is neat. + int next_source_id = 0; + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); file_it++) + if (! file_it->second->source_id_) + file_it->second->source_id_ = next_source_id++; +} + +bool Module::ReportError() { + fprintf(stderr, "error writing symbol file: %s\n", + strerror (errno)); + return false; +} + +bool Module::Write(FILE *stream) { + if (0 > fprintf(stream, "MODULE %s %s %s %s\n", + os_.c_str(), architecture_.c_str(), id_.c_str(), + name_.c_str())) + return ReportError(); + + // Write out files. + AssignSourceIds(); + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); file_it++) { + File *file = file_it->second; + if (file->source_id_ >= 0) { + if (0 > fprintf(stream, "FILE %d %s\n", + file->source_id_, file->name_.c_str())) + return ReportError(); + } + } + + // Write out functions and their lines. + for (vector::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); func_it++) { + Function *func = *func_it; + if (0 > fprintf(stream, "FUNC %lx %lx %lu %s\n", + (unsigned long) (func->address_ - load_address_), + (unsigned long) func->size_, + (unsigned long) func->parameter_size_, + func->name_.c_str())) + return ReportError(); + for (vector::iterator line_it = func->lines_.begin(); + line_it != func->lines_.end(); line_it++) + if (0 > fprintf(stream, "%lx %lx %d %d\n", + (unsigned long) (line_it->address_ - load_address_), + (unsigned long) line_it->size_, + line_it->number_, + line_it->file_->source_id_)) + return ReportError(); + } + + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/module.h b/toolkit/crashreporter/google-breakpad/src/common/linux/module.h new file mode 100644 index 000000000000..b91c0f9062a9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/module.h @@ -0,0 +1,193 @@ +// Copyright (c) 2009, Google Inc. -*- mode: c++ -*- +// 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. + +// module.h: defines google_breakpad::Module, for writing breakpad symbol files + +#ifndef COMMON_LINUX_MODULE_H__ +#define COMMON_LINUX_MODULE_H__ + +#include +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::string; +using std::vector; +using std::map; + +// A Module represents the contents of a module, and supports methods +// for adding information produced by parsing STABS or DWARF data +// --- possibly both from the same file --- and then writing out the +// unified contents as a Breakpad-format symbol file. +class Module { + public: + // The type of addresses and sizes in a symbol table. + typedef u_int64_t Address; + struct File; + struct Function; + struct Line; + + // Addresses appearing in File, Function, and Line structures are + // absolute, not relative to the the module's load address. That + // is, if the module were loaded at its nominal load address, the + // addresses would be correct. + + // A source file. + struct File { + // The name of the source file. + string name_; + + // The file's source id. The Write member function clears this + // field and assigns source ids a fresh, so any value placed here + // before calling Write will be lost. + int source_id_; + }; + + // A function. + struct Function { + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Function *x, const Function *y) { + return x->address_ < y->address_; + } + + // The function's name. + string name_; + + // The start address and length of the function's code. + Address address_, size_; + + // The function's parameter size. + Address parameter_size_; + + // Source lines belonging to this function, sorted by increasing + // address. + vector lines_; + }; + + // A source line. + struct Line { + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Module::Line &x, const Module::Line &y) { + return x.address_ < y.address_; + } + + Address address_, size_; // The address and size of the line's code. + File *file_; // The source file. + int number_; // The source line number. + }; + + // Create a new module with the given name, operating system, + // architecture, and ID string. + Module(const string &name, const string &os, const string &architecture, + const string &id); + ~Module(); + + // Set the module's load address to LOAD_ADDRESS; addresses given + // for functions and lines will be written to the Breakpad symbol + // file as offsets from this address. Construction initializes this + // module's load address to zero: addresses written to the symbol + // file will be the same as they appear in the File and Line + // structures. + void SetLoadAddress(Address load_address); + + // Add FUNCTION to the module. + // Destroying this module frees all Function objects that have been + // added with this function. + void AddFunction(Function *function); + + // Add all the functions in [BEGIN,END) to the module. + // Destroying this module frees all Function objects that have been + // added with this function. + void AddFunctions(vector::iterator begin, + vector::iterator end); + + // If this module has a file named NAME, return a pointer to it. If + // it has none, then create one and return a pointer to the new + // file. Destroying this module frees all File objects that have + // been created using this function, or with Insert. + File *FindFile(const string &name); + File *FindFile(const char *name); + + // Write this module to STREAM in the breakpad symbol format. + // Return true if all goes well, or false if an error occurs. This + // method writes out: + // - a header based on the values given to the constructor, + // - the source files added via FindFile, and finally + // - the functions added via AddFunctions, each with its lines. + // Addresses in the output are all relative to the load address + // established by SetLoadAddress. + bool Write(FILE *stream); + +private: + + // Find those files in this module that are actually referred to by + // functions' line number data, and assign them source id numbers. + // Set the source id numbers for all other files --- unused by the + // source line data --- to -1. We do this before writing out the + // symbol file, at which point we omit any unused files. + void AssignSourceIds(); + + // Report an error that has occurred writing the symbol file, using + // errno to find the appropriate cause. Return false. + static bool ReportError(); + + // Module header entries. + string name_, os_, architecture_, id_; + + // The module's nominal load address. Addresses for functions and + // lines are absolute, assuming the module is loaded at this + // address. + Address load_address_; + + // Relation for maps whose keys are strings shared with some other + // structure. + struct CompareStringPtrs { + bool operator()(const string *x, const string *y) { return *x < *y; }; + }; + + // A map from filenames to File structures. The map's keys are + // pointers to the Files' names. + typedef map FileByNameMap; + + // The module owns all the files and functions that have been added + // to it; destroying the module frees the Files and Functions these + // point to. + FileByNameMap files_; // This module's source files. + vector functions_; // This module's functions. +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MODULE_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc new file mode 100644 index 000000000000..570733613d7b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc @@ -0,0 +1,195 @@ +// Copyright 2009 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. + +// This file implements the google_breakpad::StabsReader class. + +#include +#include +#include +#include + +#include "common/linux/stabs_reader.h" + +namespace google_breakpad { + +StabsReader::StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + StabsHandler *handler) : + stabstr_(stabstr), + stabstr_size_(stabstr_size), + handler_(handler), + symbol_(NULL), + current_source_file_(NULL) { + symbols_ = reinterpret_cast(stab); + symbols_end_ = symbols_ + (stab_size / sizeof (*symbols_)); +} + +const char *StabsReader::SymbolString() { + ptrdiff_t offset = symbol_->n_un.n_strx; + if (offset < 0 || (size_t) offset >= stabstr_size_) { + handler_->Warning("symbol %d: name offset outside the string section", + symbol_ - symbols_); + // Return our null string, to keep our promise about all names being + // taken from the string section. + offset = 0; + } + return reinterpret_cast(stabstr_ + offset); +} + +bool StabsReader::Process() { + symbol_ = symbols_; + while (symbol_ < symbols_end_) { + if (symbol_->n_type == N_SO) { + if (! ProcessCompilationUnit()) + return false; + } else + symbol_++; + } + return true; +} + +bool StabsReader::ProcessCompilationUnit() { + assert(symbol_ < symbols_end_ && symbol_->n_type == N_SO); + + // There may be an N_SO entry whose name ends with a slash, + // indicating the directory in which the compilation occurred. + // The build directory defaults to NULL. + const char *build_directory = NULL; + { + const char *name = SymbolString(); + if (name[0] && name[strlen(name) - 1] == '/') { + build_directory = name; + symbol_++; + } + } + + // We expect to see an N_SO entry with a filename next, indicating + // the start of the compilation unit. + { + if (symbol_ >= symbols_end_ || symbol_->n_type != N_SO) + return true; + const char *name = SymbolString(); + if (name[0] == '\0') + return true; + current_source_file_ = name; + } + + if (! handler_->StartCompilationUnit(current_source_file_, + SymbolValue(), + build_directory)) + return false; + + symbol_++; + + // The STABS documentation says that some compilers may emit + // additional N_SO units with names immediately following the first, + // and that they should be ignored. However, the original Breakpad + // STABS reader doesn't ignore them, so we won't either. + + // Process the body of the compilation unit, up to the next N_SO. + while (symbol_ < symbols_end_ && symbol_->n_type != N_SO) { + if (symbol_->n_type == N_FUN) { + if (! ProcessFunction()) + return false; + } else + // Ignore anything else. + symbol_++; + } + + // An N_SO with an empty name indicates the end of the compilation + // unit. Default to zero. + uint64_t ending_address = 0; + if (symbol_ < symbols_end_) { + assert(symbol_->n_type == N_SO); + const char *name = SymbolString(); + if (name[0] == '\0') { + ending_address = SymbolValue(); + symbol_++; + } + } + + if (! handler_->EndCompilationUnit(ending_address)) + return false; + + return true; +} + +bool StabsReader::ProcessFunction() { + assert(symbol_ < symbols_end_ && symbol_->n_type == N_FUN); + + uint64_t function_address = SymbolValue(); + // The STABS string for an N_FUN entry is the name of the function, + // followed by a colon, followed by type information for the + // function. We want to pass the name alone to StartFunction. + const char *stab_string = SymbolString(); + const char *name_end = strchr(stab_string, ':'); + if (! name_end) + name_end = stab_string + strlen(stab_string); + std::string name(stab_string, name_end - stab_string); + if (! handler_->StartFunction(name, function_address)) + return false; + symbol_++; + + while (symbol_ < symbols_end_) { + if (symbol_->n_type == N_SO || symbol_->n_type == N_FUN) + break; + else if (symbol_->n_type == N_SLINE) { + // The value of an N_SLINE entry is the offset of the line from + // the function's start address. + uint64_t line_address = function_address + SymbolValue(); + // The n_desc of a N_SLINE entry is the line number. It's a + // signed 16-bit field; line numbers from 32768 to 65535 are + // stored as n-65536. + uint16_t line_number = symbol_->n_desc; + if (! handler_->Line(line_address, current_source_file_, line_number)) + return false; + symbol_++; + } else if (symbol_->n_type == N_SOL) { + current_source_file_ = SymbolString(); + symbol_++; + } else + // Ignore anything else. + symbol_++; + } + + // If there is a subsequent N_SO or N_FUN entry, its address is our + // end address. + uint64_t ending_address = 0; + if (symbol_ < symbols_end_) { + assert(symbol_->n_type == N_SO || symbol_->n_type == N_FUN); + ending_address = SymbolValue(); + // Note: we do not increment symbol_ here, since we haven't consumed it. + } + + if (! handler_->EndFunction(ending_address)) + return false; + + return true; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h b/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h new file mode 100644 index 000000000000..7ebc30b1fd95 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h @@ -0,0 +1,188 @@ +// Copyright 2009 Google Inc. All Rights Reserved. -*- mode: c++ -*- +// +// 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. + +// This file contains definitions related to the STABS reader and +// its handler interfaces. +// A description of the STABS debugging format can be found at +// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html +// The comments here assume you understand the format. +// +// This reader assumes that the system's and +// headers accurately describe the layout of the STABS data; this code +// is not cross-platform safe. + +#ifndef COMMON_LINUX_STABS_READER_H__ +#define COMMON_LINUX_STABS_READER_H__ + +#include +#include +#include + +#include + +namespace google_breakpad { + +class StabsHandler; + +class StabsReader { + public: + // Create a reader for the STABS debug information whose .stab + // section is the STAB_SIZE bytes at STAB, and whose .stabstr + // section is the STABSTR_SIZE bytes at STABSTR. The reader will + // call the methods of HANDLER to report the information it finds, + // when the reader's 'process' method is called. + // + // Note that, in ELF, the .stabstr section should be found using the + // 'sh_link' field of the .stab section header, not by name. + StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + StabsHandler *handler); + + // Process the STAB data, calling the handler's methods to report + // what we find. While the handler functions return true, continue + // to process until we reach the end of the section. If we + // processed the entire section and all handlers returned true, + // return true. If any handler returned false, return false. + bool Process(); + + private: + // Return the name of the current symbol. + const char *SymbolString(); + + // Return the value of the current symbol. + const uint64_t SymbolValue() { + return symbol_->n_value; + } + + // Process a compilation unit starting at symbol_. Return true + // to continue processing, or false to abort. + bool ProcessCompilationUnit(); + + // Process a function in current_source_file_ starting at symbol_. + // Return true to continue processing, or false to abort. + bool ProcessFunction(); + + // The debugging information we're reading. + const struct nlist *symbols_, *symbols_end_; + const uint8_t *stabstr_; + size_t stabstr_size_; + + StabsHandler *handler_; + + // The current symbol we're processing. + const struct nlist *symbol_; + + // The current source file name. + const char *current_source_file_; +}; + +// Consumer-provided callback structure for the STABS reader. +// Clients of the STABS reader provide an instance of this structure. +// The reader then invokes the methods of that instance to report the +// information it finds. +// +// The default definitions of the methods do nothing. +class StabsHandler { + public: + StabsHandler() { } + virtual ~StabsHandler() { } + + // Some general notes about the handler callback functions: + + // Processing proceeds until the end of the .stabs section, or until + // one of these functions returns false. + + // The addresses given are as reported in the STABS info, without + // regard for whether the module may be loaded at different + // addresses at different times (a shared library, say). When + // processing STABS from an ELF shared library, the addresses given + // all assume the library is loaded at its nominal load address. + // They are *not* offsets from the nominal load address. If you + // want offsets, you must subtract off the library's nominal load + // address. + + // The arguments to these functions named FILENAME are all + // references to strings stored in the .stabstr section. Because + // both the Linux and Solaris linkers factor out duplicate strings + // from the .stabstr section, the consumer can assume that if two + // FILENAME values are different addresses, they represent different + // file names. + // + // Thus, it's safe to use (say) std::map, which does + // address comparisons. Since all the pointers are into the array + // holding the .stabstr section's contents, comparing them produces + // predictable results. + + // Begin processing a compilation unit whose main source file is + // named FILENAME, and whose base address is ADDRESS. If + // BUILD_DIRECTORY is non-NULL, it is the name of the build + // directory in which the compilation occurred. + virtual bool StartCompilationUnit(const char *filename, uint64_t address, + const char *build_directory) { + return true; + } + + // Finish processing the compilation unit. If END_ADDRESS is + // non-zero, it is the ending address of the compilation unit. This + // information may not be available, in which case the consumer must + // infer it by other means. + virtual bool EndCompilationUnit(uint64_t address) { return true; } + + // Begin processing a function named NAME, whose starting address is + // ADDRESS. This function belongs to the compilation unit that was + // most recently started but not ended. + // + // Note that, unlike filenames, NAME is not a pointer into the + // .stabstr section; this is because the name as it appears in the + // STABS data is followed by type information. The value passed to + // StartFunction is the function name alone. + virtual bool StartFunction(const std::string &name, uint64_t address) { + return true; + } + + // Finishing processing the function. If END_ADDRESS is non-zero, + // it is the ending address for the function. This information may + // not be available, in which case the consumer must infer it by + // other means. + virtual bool EndFunction(uint64_t address) { return true; } + + // Report that the code at ADDRESS is attributable to line NUMBER of + // the source file named FILENAME. The caller must infer the ending + // address of the line. + virtual bool Line(uint64_t address, const char *filename, int number) { + return true; + } + + // Report a warning. FORMAT is a printf-like format string, + // specifying how to format the subsequent arguments. + virtual void Warning(const char *format, ...) { } +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_STABS_READER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm b/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm index 567855e0c332..d9c791cc1a2a 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm @@ -31,6 +31,8 @@ // Simple string dictionary that does not allocate memory // +#include + #import "SimpleStringDictionary.h" namespace google_breakpad { diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h index 12d8f7d6e755..5ed3e3b6f6ca 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h @@ -37,7 +37,7 @@ // This will map from an architecture string to a SectionMap, which // will contain the offsets for all the sections in the dictionary -typedef hash_map ArchSectionMap; +typedef map ArchSectionMap; @interface DumpSymbols : NSObject { @protected diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm index 88f1f4f15da7..8f0b9fe15541 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm @@ -68,15 +68,6 @@ static NSString *kHeaderCPUTypeKey = @"cpuType"; // for pruning out extraneous non-function symbols. static const int kTextSection = 1; -namespace __gnu_cxx { -template<> - struct hash { - size_t operator()(const std::string& k) const { - return hash< const char* >()( k.c_str() ); - } -}; -} - // Dump FunctionMap to stdout. Print address, function name, file // name, line number, lowpc, and highpc if available. void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { @@ -321,13 +312,16 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { //============================================================================= - (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset { + BOOL loadedStabs = [self loadSTABSSymbolInfo:base offset:offset]; + NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_]; + BOOL loadedDWARF = NO; if ([archSections objectForKey:@"__DWARF__debug_info"]) { // Treat this this as debug information - return [self loadDWARFSymbolInfo:base offset:offset]; + loadedDWARF = [self loadDWARFSymbolInfo:base offset:offset]; } - return [self loadSTABSSymbolInfo:base offset:offset]; + return loadedDWARF || loadedStabs; } //============================================================================= @@ -342,11 +336,15 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { section *dbgInfoSection = [[archSections objectForKey:@"__DWARF__debug_info"] sectionPointer]; uint32_t debugInfoSize = SwapLongIfNeeded(dbgInfoSection->size); - // i think this will break if run on a big-endian machine +#if __BIG_ENDIAN__ + dwarf2reader::ByteReader byte_reader(swap ? + dwarf2reader::ENDIANNESS_LITTLE : + dwarf2reader::ENDIANNESS_BIG); +#elif __LITTLE_ENDIAN__ dwarf2reader::ByteReader byte_reader(swap ? dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE); - +#endif uint64_t dbgOffset = 0; dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:architecture_]; diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h index 7ccd0fe68e2b..daf9120647b4 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h @@ -37,9 +37,11 @@ inline uint8 ByteReader::ReadOneByte(const char* buffer) const { return buffer[0]; } -inline uint16 ByteReader::ReadTwoBytes(const char* buffer) const { - const uint16 buffer0 = static_cast(buffer[0]); - const uint16 buffer1 = static_cast(buffer[1]); +inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint16 buffer0 = buffer[0]; + const uint16 buffer1 = buffer[1]; if (endian_ == ENDIANNESS_LITTLE) { return buffer0 | buffer1 << 8; } else { @@ -47,11 +49,13 @@ inline uint16 ByteReader::ReadTwoBytes(const char* buffer) const { } } -inline uint64 ByteReader::ReadFourBytes(const char* buffer) const { - const uint32 buffer0 = static_cast(buffer[0]); - const uint32 buffer1 = static_cast(buffer[1]); - const uint32 buffer2 = static_cast(buffer[2]); - const uint32 buffer3 = static_cast(buffer[3]); +inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint32 buffer0 = buffer[0]; + const uint32 buffer1 = buffer[1]; + const uint32 buffer2 = buffer[2]; + const uint32 buffer3 = buffer[3]; if (endian_ == ENDIANNESS_LITTLE) { return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; } else { @@ -59,15 +63,17 @@ inline uint64 ByteReader::ReadFourBytes(const char* buffer) const { } } -inline uint64 ByteReader::ReadEightBytes(const char* buffer) const { - const uint64 buffer0 = static_cast(buffer[0]); - const uint64 buffer1 = static_cast(buffer[1]); - const uint64 buffer2 = static_cast(buffer[2]); - const uint64 buffer3 = static_cast(buffer[3]); - const uint64 buffer4 = static_cast(buffer[4]); - const uint64 buffer5 = static_cast(buffer[5]); - const uint64 buffer6 = static_cast(buffer[6]); - const uint64 buffer7 = static_cast(buffer[7]); +inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint64 buffer0 = buffer[0]; + const uint64 buffer1 = buffer[1]; + const uint64 buffer2 = buffer[2]; + const uint64 buffer3 = buffer[3]; + const uint64 buffer4 = buffer[4]; + const uint64 buffer5 = buffer[5]; + const uint64 buffer6 = buffer[6]; + const uint64 buffer7 = buffer[7]; if (endian_ == ENDIANNESS_LITTLE) { return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc index 823cf2b6b86a..906fbb3229bc 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc @@ -26,8 +26,9 @@ // (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 "common/mac/dwarf/bytereader-inl.h" +#include +#include "common/mac/dwarf/bytereader-inl.h" #include "common/mac/dwarf/bytereader.h" namespace dwarf2reader { diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc index 6e7a2f1d1daf..e43029cc92cb 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc @@ -26,7 +26,8 @@ // (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 +#include + #include #include @@ -35,17 +36,6 @@ #include "common/mac/dwarf/bytereader.h" #include "common/mac/dwarf/line_state_machine.h" -namespace __gnu_cxx -{ - template<> struct hash< std::string > - { - size_t operator()( const std::string& x ) const - { - return hash< const char* >()( x.c_str() ); - } - }; -} - namespace dwarf2reader { // Read a DWARF2/3 initial length field from START, using READER, and diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h index f27cdac7dc92..cb47d97f6565 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h @@ -36,8 +36,8 @@ #ifndef COMMON_MAC_DWARF_DWARF2READER_H__ #define COMMON_MAC_DWARF_DWARF2READER_H__ -#include #include +#include #include #include #include @@ -46,7 +46,6 @@ #include "common/mac/dwarf/types.h" using namespace std; -using namespace __gnu_cxx; namespace dwarf2reader { struct LineStateMachine; @@ -56,7 +55,7 @@ class LineInfoHandler; // This maps from a string naming a section to a pair containing a // the data for the section, and the size of the section. -typedef hash_map > SectionMap; +typedef map > SectionMap; typedef list > AttributeList; typedef AttributeList::iterator AttributeIterator; typedef AttributeList::const_iterator ConstAttributeIterator; diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc index b6d3f0fe197f..04165dc0c1f6 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc @@ -29,26 +29,17 @@ // This is a client for the dwarf2reader to extract function and line // information from the debug info. +#include + #include #include #include - #include "common/mac/dwarf/functioninfo.h" #include "common/mac/dwarf/bytereader.h" -namespace __gnu_cxx -{ - template<> - struct hash - { - size_t operator()(const std::string& k) const; - }; -} - - namespace dwarf2reader { // Given an offset value, its form, and the base offset of the diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc index 0392627f66a5..9cd0d2e393b1 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc @@ -118,11 +118,13 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { return false; } - DWORD source_id; - if (FAILED(line->get_sourceFileId(&source_id))) { + DWORD dia_source_id; + if (FAILED(line->get_sourceFileId(&dia_source_id))) { fprintf(stderr, "failed to get line source file id\n"); return false; } + // duplicate file names are coalesced to share one ID + DWORD source_id = GetRealFileID(dia_source_id); DWORD line_num; if (FAILED(line->get_lineNumber(&line_num))) { @@ -136,17 +138,18 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { return true; } -bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) { +bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, + IDiaSymbol *block) { // The function format is: // FUNC
DWORD rva; - if (FAILED(function->get_relativeVirtualAddress(&rva))) { + if (FAILED(block->get_relativeVirtualAddress(&rva))) { fprintf(stderr, "couldn't get rva\n"); return false; } ULONGLONG length; - if (FAILED(function->get_length(&length))) { + if (FAILED(block->get_length(&length))) { fprintf(stderr, "failed to get function length\n"); return false; } @@ -215,7 +218,16 @@ bool PDBSourceLineWriter::PrintSourceFiles() { return false; } - fwprintf(output_, L"FILE %d %s\n", file_id, file_name); + wstring file_name_string(file_name); + if (!FileIDIsCached(file_name_string)) { + // this is a new file name, cache it and output a FILE line. + CacheFileID(file_name_string, file_id); + fwprintf(output_, L"FILE %d %s\n", file_id, file_name); + } else { + // this file name has already been seen, just save this + // ID for later lookup. + StoreDuplicateFileID(file_name_string, file_id); + } file.Release(); } compiland.Release(); @@ -255,7 +267,7 @@ bool PDBSourceLineWriter::PrintFunctions() { // that PDBSourceLineWriter will output either a FUNC or PUBLIC line, // but not both. if (tag == SymTagFunction) { - if (!PrintFunction(symbol)) { + if (!PrintFunction(symbol, symbol)) { return false; } } else if (tag == SymTagPublicSymbol) { @@ -266,6 +278,64 @@ bool PDBSourceLineWriter::PrintFunctions() { symbol.Release(); } while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1); + // When building with PGO, the compiler can split functions into + // "hot" and "cold" blocks, and move the "cold" blocks out to separate + // pages, so the function can be noncontiguous. To find these blocks, + // we have to iterate over all the compilands, and then find blocks + // that are children of them. We can then find the lexical parents + // of those blocks and print out an extra FUNC line for blocks + // that are not contained in their parent functions. + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed on the global\n"); + return false; + } + + CComPtr compiland; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr blocks; + if (FAILED(compiland->findChildren(SymTagBlock, NULL, + nsNone, &blocks))) { + fprintf(stderr, "findChildren failed on a compiland\n"); + return false; + } + + CComPtr block; + while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { + // find this block's lexical parent function + CComPtr parent; + DWORD tag; + if (SUCCEEDED(block->get_lexicalParent(&parent)) && + SUCCEEDED(parent->get_symTag(&tag)) && + tag == SymTagFunction) { + // now get the block's offset and the function's offset and size, + // and determine if the block is outside of the function + DWORD func_rva, block_rva; + ULONGLONG func_length; + if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) && + SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) && + SUCCEEDED(parent->get_length(&func_length))) { + if (block_rva < func_rva || block_rva > (func_rva + func_length)) { + if (!PrintFunction(parent, block)) { + return false; + } + } + } + } + parent.Release(); + block.Release(); + } + blocks.Release(); + compiland.Release(); + } + return true; } diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h index e91d0737c93d..8524d8b0532f 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h @@ -35,6 +35,7 @@ #include +#include #include struct IDiaEnumLineNumbers; @@ -44,6 +45,7 @@ struct IDiaSymbol; namespace google_breakpad { using std::wstring; +using stdext::hash_map; // A structure that carries information that identifies a pdb file. struct PDBModuleInfo { @@ -111,8 +113,11 @@ class PDBSourceLineWriter { bool PrintLines(IDiaEnumLineNumbers *lines); // Outputs a function address and name, followed by its source line list. + // block can be the same object as function, or it can be a reference + // to a code block that is lexically part of this function, but + // resides at a separate address. // Returns true on success. - bool PrintFunction(IDiaSymbol *function); + bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block); // Outputs all functions as described above. Returns true on success. bool PrintFunctions(); @@ -134,6 +139,37 @@ class PDBSourceLineWriter { // its uuid and age. bool PrintPDBInfo(); + // Returns true if this filename has already been seen, + // and an ID is stored for it, or false if it has not. + bool FileIDIsCached(const wstring &file) { + return unique_files_.find(file) != unique_files_.end(); + }; + + // Cache this filename and ID for later reuse. + void CacheFileID(const wstring &file, DWORD id) { + unique_files_[file] = id; + }; + + // Store this ID in the cache as a duplicate for this filename. + void StoreDuplicateFileID(const wstring &file, DWORD id) { + hash_map::iterator iter = unique_files_.find(file); + if (iter != unique_files_.end()) { + // map this id to the previously seen one + file_ids_[id] = iter->second; + } + }; + + // Given a file's unique ID, return the ID that should be used to + // reference it. There may be multiple files with identical filenames + // but different unique IDs. The cache attempts to coalesce these into + // one ID per unique filename. + DWORD GetRealFileID(DWORD id) { + hash_map::iterator iter = file_ids_.find(id); + if (iter == file_ids_.end()) + return id; + return iter->second; + }; + // Returns the function name for a symbol. If possible, the name is // undecorated. If the symbol's decorated form indicates the size of // parameters on the stack, this information is returned in stack_param_size. @@ -153,6 +189,13 @@ class PDBSourceLineWriter { // The current output file for this WriteMap invocation. FILE *output_; + // There may be many duplicate filenames with different IDs. + // This maps from the DIA "unique ID" to a single ID per unique + // filename. + hash_map file_ids_; + // This maps unique filenames to file IDs. + hash_map unique_files_; + // Disallow copy ctor and operator= PDBSourceLineWriter(const PDBSourceLineWriter&); void operator=(const PDBSourceLineWriter&); diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h index 7fd4bc4fe75b..f052401c383d 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h @@ -94,8 +94,11 @@ typedef enum { /* EXCEPTION_PRIV_INSTRUCTION */ MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW = 0xc00000fd, /* EXCEPTION_STACK_OVERFLOW */ - MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194 + MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194, /* EXCEPTION_POSSIBLE_DEADLOCK */ + MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363 + /* Per http://support.microsoft.com/kb/185294, + generated by Visual C++ compiler */ } MDExceptionCodeWin; diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h index 38759579f5fc..c01cc685e08f 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h @@ -33,28 +33,14 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ #define GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ -// TODO: Platforms that have no hash_map can use map, at the likely cost of -// performance. -#ifdef __SUNPRO_CC -#define BSLR_NO_HASH_MAP -#endif // __SUNPRO_CC - -#ifdef BSLR_NO_HASH_MAP #include -#else // BSLR_NO_HASH_MAP -#include -#endif // BSLR_NO_HASH_MAP #include "google_breakpad/processor/source_line_resolver_interface.h" namespace google_breakpad { using std::string; -#ifdef BSLR_NO_HASH_MAP using std::map; -#else // BSLR_NO_HASH_MAP -using __gnu_cxx::hash_map; -#endif // BSLR_NO_HASH_MAP class BasicSourceLineResolver : public SourceLineResolverInterface { public: @@ -85,23 +71,13 @@ class BasicSourceLineResolver : public SourceLineResolverInterface { struct Function; struct PublicSymbol; struct File; -#ifdef BSLR_NO_HASH_MAP struct CompareString { bool operator()(const string &s1, const string &s2) const; }; -#else // BSLR_NO_HASH_MAP - struct HashString { - size_t operator()(const string &s) const; - }; -#endif // BSLR_NO_HASH_MAP class Module; // All of the modules we've loaded -#ifdef BSLR_NO_HASH_MAP typedef map ModuleMap; -#else // BSLR_NO_HASH_MAP - typedef hash_map ModuleMap; -#endif // BSLR_NO_HASH_MAP ModuleMap *modules_; // Disallow unwanted copy ctor and assignment operator diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h index f08a1c1833cc..d3a7b92b1ca7 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h @@ -639,6 +639,46 @@ class MinidumpException : public MinidumpStream { MinidumpContext* context_; }; +// MinidumpAssertion wraps MDRawAssertionInfo, which contains information +// about an assertion that caused the minidump to be generated. +class MinidumpAssertion : public MinidumpStream { + public: + virtual ~MinidumpAssertion(); + + const MDRawAssertionInfo* assertion() const { + return valid_ ? &assertion_ : NULL; + } + + string expression() const { + return valid_ ? expression_ : ""; + } + + string function() const { + return valid_ ? function_ : ""; + } + + string file() const { + return valid_ ? file_ : ""; + } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const u_int32_t kStreamType = MD_ASSERTION_INFO_STREAM; + + explicit MinidumpAssertion(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + MDRawAssertionInfo assertion_; + string expression_; + string function_; + string file_; +}; + // MinidumpSystemInfo wraps MDRawSystemInfo and provides information about // the system on which the minidump was generated. See also MinidumpMiscInfo. @@ -788,6 +828,7 @@ class Minidump { MinidumpModuleList* GetModuleList(); MinidumpMemoryList* GetMemoryList(); MinidumpException* GetException(); + MinidumpAssertion* GetAssertion(); MinidumpSystemInfo* GetSystemInfo(); MinidumpMiscInfo* GetMiscInfo(); MinidumpBreakpadInfo* GetBreakpadInfo(); diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h index f313bf71df25..756a868beb2f 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h @@ -141,6 +141,11 @@ class MinidumpProcessor { return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED); } + // Returns a textual representation of an assertion included + // in the minidump. Returns an empty string if this information + // does not exist or cannot be determined. + static string GetAssertion(Minidump *dump); + private: SymbolSupplier *supplier_; SourceLineResolverInterface *resolver_; diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h index afbbb1937dbe..230142278b9f 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h @@ -36,8 +36,9 @@ #include #include -#include "google_breakpad/processor/system_info.h" #include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/system_info.h" +#include "google_breakpad/processor/minidump.h" namespace google_breakpad { @@ -60,8 +61,12 @@ class ProcessState { bool crashed() const { return crashed_; } string crash_reason() const { return crash_reason_; } u_int64_t crash_address() const { return crash_address_; } + string assertion() const { return assertion_; } int requesting_thread() const { return requesting_thread_; } const vector* threads() const { return &threads_; } + const vector* thread_memory_regions() const { + return &thread_memory_regions_; + } const SystemInfo* system_info() const { return &system_info_; } const CodeModules* modules() const { return modules_; } @@ -88,6 +93,11 @@ class ProcessState { // this will be the address of the instruction that caused the fault. u_int64_t crash_address_; + // If there was an assertion that was hit, a textual representation + // of that assertion, possibly including the file and line at which + // it occurred. + string assertion_; + // The index of the thread that requested a dump be written in the // threads vector. If a dump was produced as a result of a crash, this // will point to the thread that crashed. If the dump was produced as @@ -101,6 +111,7 @@ class ProcessState { // Stacks for each thread (except possibly the exception handler // thread) at the time of the crash. vector threads_; + vector thread_memory_regions_; // OS and CPU information. SystemInfo system_info_; diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h index 70823b9cd716..3d3003b74641 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h @@ -58,7 +58,23 @@ struct StackFrameX86 : public StackFrame { CONTEXT_VALID_ALL = -1 }; - StackFrameX86() : context(), context_validity(CONTEXT_VALID_NONE) {} + // Indicates how well we trust the instruction pointer we derived + // during stack walking. Since the stack walker can resort to + // stack scanning, we can wind up with dubious frames. + // In rough order of "trust metric". + enum FrameTrust { + FRAME_TRUST_NONE, // Unknown + FRAME_TRUST_SCAN, // Scanned the stack, found this + FRAME_TRUST_CFI_SCAN, // Scanned the stack using call frame info, found this + FRAME_TRUST_FP, // Derived from frame pointer + FRAME_TRUST_CFI, // Derived from call frame info + FRAME_TRUST_CONTEXT // Given as instruction pointer in a context + }; + + StackFrameX86() + : context(), + context_validity(CONTEXT_VALID_NONE), + trust(FRAME_TRUST_NONE) {} // Register state. This is only fully valid for the topmost frame in a // stack. In other frames, the values of nonvolatile registers may be @@ -70,6 +86,10 @@ struct StackFrameX86 : public StackFrame { // the OR operator doesn't work well with enumerated types. This indicates // which fields in context are valid. int context_validity; + + // Amount of trust the stack walker has in the instruction pointer + // of this frame. + FrameTrust trust; }; struct StackFramePPC : public StackFrame { diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h index c463fd802df7..90274aae10ce 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h @@ -42,6 +42,7 @@ #define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ #include +#include "google_breakpad/common/breakpad_types.h" namespace google_breakpad { @@ -95,6 +96,16 @@ class Stackwalker { SymbolSupplier *supplier, SourceLineResolverInterface *resolver); + // This can be used to filter out potential return addresses when + // the stack walker resorts to stack scanning. + // Returns true if any of: + // * This address is within a loaded module, but we don't have symbols + // for that module. + // * This address is within a loaded module for which we have symbols, + // and falls inside a function in that module. + // Returns false otherwise. + bool InstructionAddressSeemsValid(u_int64_t address); + // Information about the system that produced the minidump. Subclasses // and the SymbolSupplier may find this information useful. const SystemInfo *system_info_; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc index fe04439ecb07..63a94a3acc9d 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc @@ -51,9 +51,6 @@ using std::map; using std::vector; using std::make_pair; -#ifndef BSLR_NO_HASH_MAP -using __gnu_cxx::hash; -#endif // BSLR_NO_HASH_MAP namespace google_breakpad { @@ -125,11 +122,7 @@ class BasicSourceLineResolver::Module { private: friend class BasicSourceLineResolver; -#ifdef BSLR_NO_HASH_MAP typedef map FileMap; -#else // BSLR_NO_HASH_MAP - typedef hash_map FileMap; -#endif // BSLR_NO_HASH_MAP // The types for stack_info_. This is equivalent to MS DIA's // StackFrameTypeEnum. Each identifies a different type of frame @@ -702,15 +695,9 @@ bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) { return true; } -#ifdef BSLR_NO_HASH_MAP bool BasicSourceLineResolver::CompareString::operator()( const string &s1, const string &s2) const { return strcmp(s1.c_str(), s2.c_str()) < 0; } -#else // BSLR_NO_HASH_MAP -size_t BasicSourceLineResolver::HashString::operator()(const string &s) const { - return hash()(s.c_str()); -} -#endif // BSLR_NO_HASH_MAP } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc index 8ce70cc878d8..ab4e428603ca 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc @@ -243,6 +243,15 @@ static string* UTF16ToUTF8(const vector& in, return out.release(); } +// Return the smaller of the number of code units in the UTF-16 string, +// not including the terminating null word, or maxlen. +static size_t UTF16codeunits(const u_int16_t *string, size_t maxlen) { + size_t count = 0; + while (count < maxlen && string[count] != 0) + count++; + return count; +} + // // MinidumpObject @@ -608,8 +617,9 @@ bool MinidumpContext::Read(u_int32_t expected_size) { } default: { - // Unknown context type - BPLOG(ERROR) << "MinidumpContext unknown context type " << + // Unknown context type - Don't log as an error yet. Let the + // caller work that out. + BPLOG(INFO) << "MinidumpContext unknown context type " << HexString(cpu_type); return false; break; @@ -1302,7 +1312,7 @@ void MinidumpThread::Print() { // -u_int32_t MinidumpThreadList::max_threads_ = 256; +u_int32_t MinidumpThreadList::max_threads_ = 4096; MinidumpThreadList::MinidumpThreadList(Minidump* minidump) @@ -1464,8 +1474,8 @@ void MinidumpThreadList::Print() { // -u_int32_t MinidumpModule::max_cv_bytes_ = 1024; -u_int32_t MinidumpModule::max_misc_bytes_ = 1024; +u_int32_t MinidumpModule::max_cv_bytes_ = 32768; +u_int32_t MinidumpModule::max_misc_bytes_ = 32768; MinidumpModule::MinidumpModule(Minidump* minidump) @@ -2424,7 +2434,7 @@ void MinidumpModuleList::Print() { // -u_int32_t MinidumpMemoryList::max_regions_ = 256; +u_int32_t MinidumpMemoryList::max_regions_ = 4096; MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) @@ -2713,8 +2723,10 @@ MinidumpContext* MinidumpException::GetContext() { scoped_ptr context(new MinidumpContext(minidump_)); + // Don't log as an error if we can still fall back on th thread's context + // (which must be possible if we got his far.) if (!context->Read(exception_.thread_context.data_size)) { - BPLOG(ERROR) << "MinidumpException cannot read context"; + BPLOG(INFO) << "MinidumpException cannot read context"; return NULL; } @@ -2765,6 +2777,109 @@ void MinidumpException::Print() { } } +// +// MinidumpAssertion +// + + +MinidumpAssertion::MinidumpAssertion(Minidump* minidump) + : MinidumpStream(minidump), + assertion_(), + expression_(), + function_(), + file_() { +} + + +MinidumpAssertion::~MinidumpAssertion() { +} + + +bool MinidumpAssertion::Read(u_int32_t expected_size) { + // Invalidate cached data. + valid_ = false; + + if (expected_size != sizeof(assertion_)) { + BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size << + " != " << sizeof(assertion_); + return false; + } + + if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) { + BPLOG(ERROR) << "MinidumpAssertion cannot read assertion"; + return false; + } + + // Each of {expression, function, file} is a UTF-16 string, + // we'll convert them to UTF-8 for ease of use. + // expression + // Since we don't have an explicit byte length for each string, + // we use UTF16codeunits to calculate word length, then derive byte + // length from that. + u_int32_t word_length = UTF16codeunits(assertion_.expression, + sizeof(assertion_.expression)); + if (word_length > 0) { + u_int32_t byte_length = word_length * 2; + vector expression_utf16(word_length); + memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); + + scoped_ptr new_expression(UTF16ToUTF8(expression_utf16, + minidump_->swap())); + expression_ = *new_expression; + } + + // assertion + word_length = UTF16codeunits(assertion_.function, + sizeof(assertion_.function)); + if (word_length) { + u_int32_t byte_length = word_length * 2; + vector function_utf16(word_length); + memcpy(&function_utf16[0], &assertion_.function[0], byte_length); + scoped_ptr new_function(UTF16ToUTF8(function_utf16, + minidump_->swap())); + function_ = *new_function; + } + + // file + word_length = UTF16codeunits(assertion_.file, + sizeof(assertion_.file)); + if (word_length > 0) { + u_int32_t byte_length = word_length * 2; + vector file_utf16(word_length); + memcpy(&file_utf16[0], &assertion_.file[0], byte_length); + scoped_ptr new_file(UTF16ToUTF8(file_utf16, + minidump_->swap())); + file_ = *new_file; + } + + if (minidump_->swap()) { + Swap(&assertion_.line); + Swap(&assertion_.type); + } + + valid_ = true; + return true; +} + +void MinidumpAssertion::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data"; + return; + } + + printf("MDAssertion\n"); + printf(" expression = %s\n", + expression_.c_str()); + printf(" function = %s\n", + function_.c_str()); + printf(" file = %s\n", + file_.c_str()); + printf(" line = %u\n", + assertion_.line); + printf(" type = %u\n", + assertion_.type); + printf("\n"); +} // // MinidumpSystemInfo @@ -3412,6 +3527,11 @@ MinidumpException* Minidump::GetException() { return GetStream(&exception); } +MinidumpAssertion* Minidump::GetAssertion() { + MinidumpAssertion* assertion; + return GetStream(&assertion); +} + MinidumpSystemInfo* Minidump::GetSystemInfo() { MinidumpSystemInfo* system_info; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc index 12a699de5c41..b586252f453a 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc @@ -44,6 +44,7 @@ using google_breakpad::MinidumpThreadList; using google_breakpad::MinidumpModuleList; using google_breakpad::MinidumpMemoryList; using google_breakpad::MinidumpException; +using google_breakpad::MinidumpAssertion; using google_breakpad::MinidumpSystemInfo; using google_breakpad::MinidumpMiscInfo; using google_breakpad::MinidumpBreakpadInfo; @@ -89,6 +90,13 @@ static bool PrintMinidumpDump(const char *minidump_file) { exception->Print(); } + MinidumpAssertion *assertion = minidump.GetAssertion(); + if (!assertion) { + BPLOG(INFO) << "minidump.GetAssertion() failed"; + } else { + assertion->Print(); + } + MinidumpSystemInfo *system_info = minidump.GetSystemInfo(); if (!system_info) { ++errors; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc index 5ca41655e4c7..24b03e9d964d 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc @@ -28,6 +28,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include #include "google_breakpad/processor/minidump_processor.h" #include "google_breakpad/processor/call_stack.h" @@ -85,6 +86,9 @@ ProcessResult MinidumpProcessor::Process( dump, &process_state->crash_address_); } + // This will just return an empty string if it doesn't exist. + process_state->assertion_ = GetAssertion(dump); + MinidumpModuleList *module_list = dump->GetModuleList(); // Put a copy of the module list into ProcessState object. This is not @@ -168,8 +172,10 @@ ProcessResult MinidumpProcessor::Process( // of the thread's own context. For the crashed thread, the thread's // own context is the state inside the exception handler. Using it // would not result in the expected stack trace from the time of the - // crash. - context = exception->GetContext(); + // crash. If the exception context is invalid, however, we fall back + // on the thread context. + MinidumpContext *ctx = exception->GetContext(); + context = ctx ? ctx : thread->GetContext(); } } @@ -206,6 +212,7 @@ ProcessResult MinidumpProcessor::Process( interrupted = true; } process_state->threads_.push_back(stack.release()); + process_state->thread_memory_regions_.push_back(thread_memory); } if (interrupted) { @@ -752,6 +759,9 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK: reason = "EXCEPTION_POSSIBLE_DEADLOCK"; break; + case MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION: + reason = "Unhandled C++ Exception"; + break; default: BPLOG(INFO) << "Unknown exception reason " << reason; break; @@ -999,4 +1009,57 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { return reason; } +// static +string MinidumpProcessor::GetAssertion(Minidump *dump) +{ + MinidumpAssertion *assertion = dump->GetAssertion(); + if (!assertion) + return ""; + + const MDRawAssertionInfo *raw_assertion = assertion->assertion(); + if (!raw_assertion) + return ""; + + string assertion_string; + switch (raw_assertion->type) { + case MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER: + assertion_string = "Invalid parameter passed to library function"; + break; + case MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL: + assertion_string = "Pure virtual function called"; + break; + default: { + char assertion_type[32]; + sprintf(assertion_type, "0x%08x", raw_assertion->type); + assertion_string = "Unknown assertion type "; + assertion_string += assertion_type; + break; + } + } + + string expression = assertion->expression(); + if (!expression.empty()) { + assertion_string.append(" " + expression); + } + + string function = assertion->function(); + if (!function.empty()) { + assertion_string.append(" in function " + function); + } + + string file = assertion->file(); + if (!file.empty()) { + assertion_string.append(", in file " + file); + } + + if (raw_assertion->line != 0) { + char assertion_line[32]; + sprintf(assertion_line, "%u", raw_assertion->line); + assertion_string.append(" at line "); + assertion_string.append(assertion_line); + } + + return assertion_string; +} + } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc index d3a830e3c7ac..3701c466b7be 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc @@ -160,6 +160,28 @@ static void PrintStack(const CallStack *stack, const string &cpu) { sequence = PrintRegister("edx", frame_x86->context.edx, sequence); sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); } + const char *trust_name; + switch (frame_x86->trust) { + case StackFrameX86::FRAME_TRUST_NONE: + trust_name = "unknown"; + break; + case StackFrameX86::FRAME_TRUST_CONTEXT: + trust_name = "given as instruction pointer in context"; + break; + case StackFrameX86::FRAME_TRUST_CFI: + trust_name = "call frame info"; + break; + case StackFrameX86::FRAME_TRUST_CFI_SCAN: + trust_name = "call frame info with scanning"; + break; + case StackFrameX86::FRAME_TRUST_FP: + trust_name = "previous frame's frame pointer"; + break; + case StackFrameX86::FRAME_TRUST_SCAN: + trust_name = "stack scanning"; + break; + } + printf("\n Found by: %s", trust_name); } else if (cpu == "ppc") { const StackFramePPC *frame_ppc = reinterpret_cast(frame); @@ -339,6 +361,11 @@ static void PrintProcessState(const ProcessState& process_state) { printf("No crash\n"); } + string assertion = process_state.assertion(); + if (!assertion.empty()) { + printf("Assertion: %s\n", assertion.c_str()); + } + // If the thread that requested the dump is known, print it first. int requesting_thread = process_state.requesting_thread(); if (requesting_thread != -1) { @@ -391,7 +418,15 @@ static void PrintProcessStateMachineReadable(const ProcessState& process_state) StripSeparator(process_state.crash_reason()).c_str(), kOutputSeparator, process_state.crash_address(), kOutputSeparator); } else { - printf("No crash%c%c", kOutputSeparator, kOutputSeparator); + // print assertion info, if available, in place of crash reason, + // instead of the unhelpful "No crash" + string assertion = process_state.assertion(); + if (!assertion.empty()) { + printf("%s%c%c", StripSeparator(assertion).c_str(), + kOutputSeparator, kOutputSeparator); + } else { + printf("No crash%c%c", kOutputSeparator, kOutputSeparator); + } } if (requesting_thread != -1) { diff --git a/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc index 957c94f2419b..54f04b780c08 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc @@ -27,6 +27,8 @@ // (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 + #include "processor/pathname_stripper.h" #include "processor/logging.h" diff --git a/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h index aa0851d97e3e..b08cd18293a9 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h +++ b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h @@ -38,6 +38,7 @@ #define PROCESSOR_POSTFIX_EVALUATOR_INL_H__ +#include #include #include "processor/postfix_evaluator.h" diff --git a/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc b/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc index 934792b38d03..1d970bad67bb 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc @@ -48,6 +48,7 @@ void ProcessState::Clear() { crashed_ = false; crash_reason_.clear(); crash_address_ = 0; + assertion_.clear(); requesting_thread_ = -1; for (vector::const_iterator iterator = threads_.begin(); iterator != threads_.end(); diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc index de67bdaa517c..96ee6db0483d 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc @@ -189,5 +189,39 @@ Stackwalker* Stackwalker::StackwalkerForCPU( return cpu_stackwalker; } +bool Stackwalker::InstructionAddressSeemsValid(u_int64_t address) { + const CodeModule *module = modules_->GetModuleForAddress(address); + if (!module) { + // not inside any loaded module + return false; + } + + if (!resolver_ || !supplier_) { + // we don't have a resolver and or symbol supplier, + // but we're inside a known module + return true; + } + + if (!resolver_->HasModule(module->code_file())) { + string symbol_data, symbol_file; + SymbolSupplier::SymbolResult symbol_result = + supplier_->GetSymbolFile(module, system_info_, + &symbol_file, &symbol_data); + + if (symbol_result != SymbolSupplier::FOUND || + !resolver_->LoadModuleUsingMapBuffer(module->code_file(), + symbol_data)) { + // we don't have symbols, but we're inside a loaded module + return true; + } + } + + StackFrame frame; + frame.module = module; + frame.instruction = address; + resolver_->FillSourceLineInfo(&frame); + // we have symbols, so return true if inside a function + return !frame.function_name.empty(); +} } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc index e39df17f26cb..aeb18a9f5118 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc @@ -79,6 +79,7 @@ StackFrame* StackwalkerX86::GetContextFrame() { // straight out of the CPU context structure. frame->context = *context_; frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL; + frame->trust = StackFrameX86::FRAME_TRUST_CONTEXT; frame->instruction = frame->context.eip; return frame; @@ -92,7 +93,7 @@ StackFrame* StackwalkerX86::GetCallerFrame( BPLOG(ERROR) << "Can't get caller frame without memory or stack"; return NULL; } - + StackFrameX86::FrameTrust trust = StackFrameX86::FRAME_TRUST_NONE; StackFrameX86 *last_frame = static_cast( stack->frames()->back()); StackFrameInfo *last_frame_info = stack_frame_info.back().get(); @@ -183,6 +184,7 @@ StackFrame* StackwalkerX86::GetCallerFrame( if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) { // FPO data available. traditional_frame = false; + trust = StackFrameX86::FRAME_TRUST_CFI; if (!last_frame_info->program_string.empty()) { // The FPO data has its own program string, which will tell us how to // get to the caller frame, and may even fill in the values of @@ -280,6 +282,7 @@ StackFrame* StackwalkerX86::GetCallerFrame( // %eip_new = *(%ebp_old + 4) // %esp_new = %ebp_old + 8 // %ebp_new = *(%ebp_old) + trust = StackFrameX86::FRAME_TRUST_FP; program_string = "$eip $ebp 4 + ^ = " "$esp $ebp 8 + = " "$ebp $ebp ^ ="; @@ -293,7 +296,26 @@ StackFrame* StackwalkerX86::GetCallerFrame( if (!evaluator.Evaluate(program_string, &dictionary_validity) || dictionary_validity.find("$eip") == dictionary_validity.end() || dictionary_validity.find("$esp") == dictionary_validity.end()) { - return NULL; + // Program string evaluation failed. It may be that %eip is not somewhere + // with stack frame info, and %ebp is pointing to non-stack memory, so + // our evaluation couldn't succeed. We'll scan the stack for a return + // address. This can happen if the stack is in a module for which + // we don't have symbols, and that module is compiled without a + // frame pointer. + u_int32_t location_start = last_frame->context.esp; + u_int32_t location, eip; + if (!ScanForReturnAddress(location_start, location, eip)) { + // if we can't find an instruction pointer even with stack scanning, + // give up. + return NULL; + } + + // This seems like a reasonable return address. Since program string + // evaluation failed, use it and set %esp to the location above the + // one where the return address was found. + dictionary["$eip"] = eip; + dictionary["$esp"] = location + 4; + trust = StackFrameX86::FRAME_TRUST_SCAN; } // If this stack frame did not use %ebp in a traditional way, locating the @@ -321,33 +343,18 @@ StackFrame* StackwalkerX86::GetCallerFrame( u_int32_t eip = dictionary["$eip"]; if (modules_ && !modules_->GetModuleForAddress(eip)) { - const int kRASearchWords = 15; - // The instruction pointer at .raSearchStart was invalid, so start // looking one 32-bit word above that location. u_int32_t location_start = dictionary[".raSearchStart"] + 4; - - for (u_int32_t location = location_start; - location <= location_start + kRASearchWords * 4; - location += 4) { - if (!memory_->GetMemoryAtAddress(location, &eip)) - break; - - if (modules_->GetModuleForAddress(eip)) { - // This is a better return address that what program string - // evaluation found. Use it, and set %esp to the location above the - // one where the return address was found. - // - // TODO(mmentovai): The return-address check can be made even - // stronger in modules for which debugging data is available. In - // that case, it's possible to check that the candidate return - // address is inside a known function. - - dictionary["$eip"] = eip; - dictionary["$esp"] = location + 4; - offset = location - location_start; - break; - } + u_int32_t location; + if (ScanForReturnAddress(location_start, location, eip)) { + // This is a better return address that what program string + // evaluation found. Use it, and set %esp to the location above the + // one where the return address was found. + dictionary["$eip"] = eip; + dictionary["$esp"] = location + 4; + offset = location - location_start; + trust = StackFrameX86::FRAME_TRUST_CFI_SCAN; } } @@ -392,6 +399,7 @@ StackFrame* StackwalkerX86::GetCallerFrame( // and fill it in. StackFrameX86 *frame = new StackFrameX86(); + frame->trust = trust; frame->context = last_frame->context; frame->context.eip = dictionary["$eip"]; frame->context.esp = dictionary["$esp"]; @@ -428,5 +436,27 @@ StackFrame* StackwalkerX86::GetCallerFrame( return frame; } +bool StackwalkerX86::ScanForReturnAddress(u_int32_t location_start, + u_int32_t &location_found, + u_int32_t &eip_found) { + const int kRASearchWords = 15; + for (u_int32_t location = location_start; + location <= location_start + kRASearchWords * 4; + location += 4) { + u_int32_t eip; + if (!memory_->GetMemoryAtAddress(location, &eip)) + break; + + if (modules_ && modules_->GetModuleForAddress(eip) && + InstructionAddressSeemsValid(eip)) { + + eip_found = eip; + location_found = location; + return true; + } + } + // nothing found + return false; +} } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h index 14b9e8b99508..d4137ebf7769 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h +++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h @@ -70,6 +70,19 @@ class StackwalkerX86 : public Stackwalker { const CallStack *stack, const vector< linked_ptr > &stack_frame_info); + // Scan the stack starting at location_start, looking for an address + // that looks like a valid instruction pointer. Addresses must + // 1) be contained in the current stack memory + // 2) pass the checks in Stackwalker::InstructionAddressSeemsValid + // + // Returns true if a valid-looking instruction pointer was found. + // When returning true, sets location_found to the address at which + // the value was found, and eip_found to the value contained at that + // location in memory. + bool ScanForReturnAddress(u_int32_t location_start, + u_int32_t &location_found, + u_int32_t &eip_found); + // Stores the CPU context corresponding to the innermost stack frame to // be returned by GetContextFrame. const MDRawContextX86 *context_; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out b/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out index 21b425de352a..be081f415ea5 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out +++ b/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out @@ -12,12 +12,16 @@ Thread 0 (crashed) eip = 0x0040429e esp = 0x0012fe84 ebp = 0x0012fe88 ebx = 0x7c80abc1 esi = 0x00000002 edi = 0x00000a28 eax = 0x00000045 ecx = 0x0012fe94 edx = 0x0042bc58 efl = 0x00010246 + Found by: given as instruction pointer in context 1 test_app.exe!main [test_app.cc : 65 + 0x4] eip = 0x00404200 esp = 0x0012fe90 ebp = 0x0012ff70 + Found by: call frame info 2 test_app.exe!__tmainCRTStartup [crt0.c : 327 + 0x11] eip = 0x004053ec esp = 0x0012ff78 ebp = 0x0012ffc0 + Found by: call frame info 3 kernel32.dll!BaseProcessStart + 0x22 eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0 + Found by: call frame info Loaded modules: 0x00400000 - 0x0042cfff test_app.exe ??? (main) diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h new file mode 100644 index 000000000000..08a3b637e149 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h @@ -0,0 +1,533 @@ +// Copyright (c) 2006, 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. + +// --- +// Author: Ray Sidney +// Revamped and reorganized by Craig Silverstein +// +// This is the file that should be included by any file which declares +// or defines a command line flag or wants to parse command line flags +// or print a program usage message (which will include information about +// flags). Executive summary, in the form of an example foo.cc file: +// +// #include "foo.h" // foo.h has a line "DECLARE_int32(start);" +// +// DEFINE_int32(end, 1000, "The last record to read"); +// DECLARE_bool(verbose); // some other file has a DEFINE_bool(verbose, ...) +// +// void MyFunc() { +// if (FLAGS_verbose) printf("Records %d-%d\n", FLAGS_start, FLAGS_end); +// } +// +// Then, at the command-line: +// ./foo --noverbose --start=5 --end=100 +// +// For more details, see +// doc/gflags.html +// +// --- A note about thread-safety: +// +// We describe many functions in this routine as being thread-hostile, +// thread-compatible, or thread-safe. Here are the meanings we use: +// +// thread-safe: it is safe for multiple threads to call this routine +// (or, when referring to a class, methods of this class) +// concurrently. +// thread-hostile: it is not safe for multiple threads to call this +// routine (or methods of this class) concurrently. In gflags, +// most thread-hostile routines are intended to be called early in, +// or even before, main() -- that is, before threads are spawned. +// thread-compatible: it is safe for multiple threads to read from +// this variable (when applied to variables), or to call const +// methods of this class (when applied to classes), as long as no +// other thread is writing to the variable or calling non-const +// methods of this class. + +#ifndef GOOGLE_GFLAGS_H_ +#define GOOGLE_GFLAGS_H_ + +#include +#include + +// We care a lot about number of bits things take up. Unfortunately, +// systems define their bit-specific ints in a lot of different ways. +// We use our own way, and have a typedef to get there. +// Note: these commands below may look like "#if 1" or "#if 0", but +// that's because they were constructed that way at ./configure time. +// Look at gflags.h.in to see how they're calculated (based on your config). +#if 1 +#include // the normal place uint16_t is defined +#endif +#if 1 +#include // the normal place u_int16_t is defined +#endif +#if 1 +#include // a third place for uint16_t or u_int16_t +#endif + +namespace google { + +#if 1 // the C99 format +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#elif 1 // the BSD format +typedef int32_t int32; +typedef u_int32_t uint32; +typedef int64_t int64; +typedef u_int64_t uint64; +#elif 0 // the windows (vc7) format +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +#error Do not know how to define a 32-bit integer quantity on your system +#endif + +// -------------------------------------------------------------------- +// To actually define a flag in a file, use DEFINE_bool, +// DEFINE_string, etc. at the bottom of this file. You may also find +// it useful to register a validator with the flag. This ensures that +// when the flag is parsed from the commandline, or is later set via +// SetCommandLineOption, we call the validation function. +// +// The validation function should return true if the flag value is valid, and +// false otherwise. If the function returns false for the new setting of the +// flag, the flag will retain its current value. If it returns false for the +// default value, InitGoogle will die. +// +// This function is safe to call at global construct time (as in the +// example below). +// +// Example use: +// static bool ValidatePort(const char* flagname, int32 value) { +// if (value > 0 && value < 32768) // value is ok +// return true; +// printf("Invalid value for --%s: %d\n", flagname, (int)value); +// return false; +// } +// DEFINE_int32(port, 0, "What port to listen on"); +// static bool dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort); + +// Returns true if successfully registered, false if not (because the +// first argument doesn't point to a command-line flag, or because a +// validator is already registered for this flag). +bool RegisterFlagValidator(const bool* flag, + bool (*validate_fn)(const char*, bool)); +bool RegisterFlagValidator(const int32* flag, + bool (*validate_fn)(const char*, int32)); +bool RegisterFlagValidator(const int64* flag, + bool (*validate_fn)(const char*, int64)); +bool RegisterFlagValidator(const uint64* flag, + bool (*validate_fn)(const char*, uint64)); +bool RegisterFlagValidator(const double* flag, + bool (*validate_fn)(const char*, double)); +bool RegisterFlagValidator(const std::string* flag, + bool (*validate_fn)(const char*, const std::string&)); + + +// -------------------------------------------------------------------- +// These methods are the best way to get access to info about the +// list of commandline flags. Note that these routines are pretty slow. +// GetAllFlags: mostly-complete info about the list, sorted by file. +// ShowUsageWithFlags: pretty-prints the list to stdout (what --help does) +// ShowUsageWithFlagsRestrict: limit to filenames with restrict as a substr +// +// In addition to accessing flags, you can also access argv[0] (the program +// name) and argv (the entire commandline), which we sock away a copy of. +// These variables are static, so you should only set them once. + +struct CommandLineFlagInfo { + std::string name; // the name of the flag + std::string type; // the type of the flag: int32, etc + std::string description; // the "help text" associated with the flag + std::string current_value; // the current value, as a string + std::string default_value; // the default value, as a string + std::string filename; // 'cleaned' version of filename holding the flag + bool has_validator_fn; // true if RegisterFlagValidator called on flag + bool is_default; // true if the flag has default value +}; + +extern void GetAllFlags(std::vector* OUTPUT); +// These two are actually defined in commandlineflags_reporting.cc. +extern void ShowUsageWithFlags(const char *argv0); // what --help does +extern void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict); + +// Create a descriptive string for a flag. +// Goes to some trouble to make pretty line breaks. +extern std::string DescribeOneFlag(const CommandLineFlagInfo& flag); + +// Thread-hostile; meant to be called before any threads are spawned. +extern void SetArgv(int argc, const char** argv); +// The following functions are thread-safe as long as SetArgv() is +// only called before any threads start. +extern const std::vector& GetArgvs(); // all of argv as a vector +extern const char* GetArgv(); // all of argv as a string +extern const char* GetArgv0(); // only argv0 +extern uint32 GetArgvSum(); // simple checksum of argv +extern const char* ProgramInvocationName(); // argv0, or "UNKNOWN" if not set +extern const char* ProgramInvocationShortName(); // basename(argv0) +// ProgramUsage() is thread-safe as long as SetUsageMessage() is only +// called before any threads start. +extern const char* ProgramUsage(); // string set by SetUsageMessage() + + +// -------------------------------------------------------------------- +// Normally you access commandline flags by just saying "if (FLAGS_foo)" +// or whatever, and set them by calling "FLAGS_foo = bar" (or, more +// commonly, via the DEFINE_foo macro). But if you need a bit more +// control, we have programmatic ways to get/set the flags as well. +// These programmatic ways to access flags are thread-safe, but direct +// access is only thread-compatible. + +// Return true iff the flagname was found. +// OUTPUT is set to the flag's value, or unchanged if we return false. +extern bool GetCommandLineOption(const char* name, std::string* OUTPUT); + +// Return true iff the flagname was found. OUTPUT is set to the flag's +// CommandLineFlagInfo or unchanged if we return false. +extern bool GetCommandLineFlagInfo(const char* name, + CommandLineFlagInfo* OUTPUT); + +// Return the CommandLineFlagInfo of the flagname. exit() if name not found. +// Example usage, to check if a flag's value is currently the default value: +// if (GetCommandLineFlagInfoOrDie("foo").is_default) ... +extern CommandLineFlagInfo GetCommandLineFlagInfoOrDie(const char* name); + +enum FlagSettingMode { + // update the flag's value (can call this multiple times). + SET_FLAGS_VALUE, + // update the flag's value, but *only if* it has not yet been updated + // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef". + SET_FLAG_IF_DEFAULT, + // set the flag's default value to this. If the flag has not yet updated + // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef") + // change the flag's current value to the new default value as well. + SET_FLAGS_DEFAULT +}; + +// Set a particular flag ("command line option"). Returns a string +// describing the new value that the option has been set to. The +// return value API is not well-specified, so basically just depend on +// it to be empty if the setting failed for some reason -- the name is +// not a valid flag name, or the value is not a valid value -- and +// non-empty else. + +// SetCommandLineOption uses set_mode == SET_FLAGS_VALUE (the common case) +extern std::string SetCommandLineOption(const char* name, const char* value); +extern std::string SetCommandLineOptionWithMode(const char* name, const char* value, + FlagSettingMode set_mode); + + +// -------------------------------------------------------------------- +// Saves the states (value, default value, whether the user has set +// the flag, registered validators, etc) of all flags, and restores +// them when the FlagSaver is destroyed. This is very useful in +// tests, say, when you want to let your tests change the flags, but +// make sure that they get reverted to the original states when your +// test is complete. +// +// Example usage: +// void TestFoo() { +// FlagSaver s1; +// FLAG_foo = false; +// FLAG_bar = "some value"; +// +// // test happens here. You can return at any time +// // without worrying about restoring the FLAG values. +// } +// +// Note: This class is marked with __attribute__((unused)) because all the +// work is done in the constructor and destructor, so in the standard +// usage example above, the compiler would complain that it's an +// unused variable. +// +// This class is thread-safe. + +class FlagSaver { + public: + FlagSaver(); + ~FlagSaver(); + + private: + class FlagSaverImpl* impl_; // we use pimpl here to keep API steady + + FlagSaver(const FlagSaver&); // no copying! + void operator=(const FlagSaver&); +} __attribute__ ((unused)); + +// -------------------------------------------------------------------- +// Some deprecated or hopefully-soon-to-be-deprecated functions. + +// This is often used for logging. TODO(csilvers): figure out a better way +extern std::string CommandlineFlagsIntoString(); +// Usually where this is used, a FlagSaver should be used instead. +extern bool ReadFlagsFromString(const std::string& flagfilecontents, + const char* prog_name, + bool errors_are_fatal); // uses SET_FLAGS_VALUE + +// These let you manually implement --flagfile functionality. +// DEPRECATED. +extern bool AppendFlagsIntoFile(const std::string& filename, const char* prog_name); +extern bool SaveCommandFlags(); // actually defined in google.cc ! +extern bool ReadFromFlagsFile(const std::string& filename, const char* prog_name, + bool errors_are_fatal); // uses SET_FLAGS_VALUE + + +// -------------------------------------------------------------------- +// Useful routines for initializing flags from the environment. +// In each case, if 'varname' does not exist in the environment +// return defval. If 'varname' does exist but is not valid +// (e.g., not a number for an int32 flag), abort with an error. +// Otherwise, return the value. NOTE: for booleans, for true use +// 't' or 'T' or 'true' or '1', for false 'f' or 'F' or 'false' or '0'. + +extern bool BoolFromEnv(const char *varname, bool defval); +extern int32 Int32FromEnv(const char *varname, int32 defval); +extern int64 Int64FromEnv(const char *varname, int64 defval); +extern uint64 Uint64FromEnv(const char *varname, uint64 defval); +extern double DoubleFromEnv(const char *varname, double defval); +extern const char *StringFromEnv(const char *varname, const char *defval); + + +// -------------------------------------------------------------------- +// The next two functions parse commandlineflags from main(): + +// Set the "usage" message for this program. For example: +// string usage("This program does nothing. Sample usage:\n"); +// usage += argv[0] + " "; +// SetUsageMessage(usage); +// Do not include commandline flags in the usage: we do that for you! +// Thread-hostile; meant to be called before any threads are spawned. +extern void SetUsageMessage(const std::string& usage); + +// Looks for flags in argv and parses them. Rearranges argv to put +// flags first, or removes them entirely if remove_flags is true. +// If a flag is defined more than once in the command line or flag +// file, the last definition is used. +// See top-of-file for more details on this function. +#ifndef SWIG // In swig, use ParseCommandLineFlagsScript() instead. +extern uint32 ParseCommandLineFlags(int *argc, char*** argv, + bool remove_flags); +#endif + + +// Calls to ParseCommandLineNonHelpFlags and then to +// HandleCommandLineHelpFlags can be used instead of a call to +// ParseCommandLineFlags during initialization, in order to allow for +// changing default values for some FLAGS (via +// e.g. SetCommandLineOptionWithMode calls) between the time of +// command line parsing and the time of dumping help information for +// the flags as a result of command line parsing. +// If a flag is defined more than once in the command line or flag +// file, the last definition is used. +extern uint32 ParseCommandLineNonHelpFlags(int *argc, char*** argv, + bool remove_flags); +// This is actually defined in commandlineflags_reporting.cc. +// This function is misnamed (it also handles --version, etc.), but +// it's too late to change that now. :-( +extern void HandleCommandLineHelpFlags(); // in commandlineflags_reporting.cc + +// Allow command line reparsing. Disables the error normally +// generated when an unknown flag is found, since it may be found in a +// later parse. Thread-hostile; meant to be called before any threads +// are spawned. +extern void AllowCommandLineReparsing(); + +// Reparse the flags that have not yet been recognized. +// Only flags registered since the last parse will be recognized. +// Any flag value must be provided as part of the argument using "=", +// not as a separate command line argument that follows the flag argument. +// Intended for handling flags from dynamically loaded libraries, +// since their flags are not registered until they are loaded. +extern uint32 ReparseCommandLineNonHelpFlags(); + + +// -------------------------------------------------------------------- +// Now come the command line flag declaration/definition macros that +// will actually be used. They're kind of hairy. A major reason +// for this is initialization: we want people to be able to access +// variables in global constructors and have that not crash, even if +// their global constructor runs before the global constructor here. +// (Obviously, we can't guarantee the flags will have the correct +// default value in that case, but at least accessing them is safe.) +// The only way to do that is have flags point to a static buffer. +// So we make one, using a union to ensure proper alignment, and +// then use placement-new to actually set up the flag with the +// correct default value. In the same vein, we have to worry about +// flag access in global destructors, so FlagRegisterer has to be +// careful never to destroy the flag-values it constructs. +// +// Note that when we define a flag variable FLAGS_, we also +// preemptively define a junk variable, FLAGS_no. This is to +// cause a link-time error if someone tries to define 2 flags with +// names like "logging" and "nologging". We do this because a bool +// flag FLAG can be set from the command line to true with a "-FLAG" +// argument, and to false with a "-noFLAG" argument, and so this can +// potentially avert confusion. +// +// We also put flags into their own namespace. It is purposefully +// named in an opaque way that people should have trouble typing +// directly. The idea is that DEFINE puts the flag in the weird +// namespace, and DECLARE imports the flag from there into the current +// namespace. The net result is to force people to use DECLARE to get +// access to a flag, rather than saying "extern bool FLAGS_whatever;" +// or some such instead. We want this so we can put extra +// functionality (like sanity-checking) in DECLARE if we want, and +// make sure it is picked up everywhere. +// +// We also put the type of the variable in the namespace, so that +// people can't DECLARE_int32 something that they DEFINE_bool'd +// elsewhere. + +class FlagRegisterer { + public: + FlagRegisterer(const char* name, const char* type, + const char* help, const char* filename, + void* current_storage, void* defvalue_storage); +}; + +extern bool FlagsTypeWarn(const char *name); + +// If your application #defines STRIP_FLAG_HELP to a non-zero value +// before #including this file, we remove the help message from the +// binary file. This can reduce the size of the resulting binary +// somewhat, and may also be useful for security reasons. + +extern const char kStrippedFlagHelp[]; + +} + +#ifndef SWIG // In swig, ignore the main flag declarations + +#if defined(STRIP_FLAG_HELP) && STRIP_FLAG_HELP > 0 +// Need this construct to avoid the 'defined but not used' warning. +#define MAYBE_STRIPPED_HELP(txt) (false ? (txt) : kStrippedFlagHelp) +#else +#define MAYBE_STRIPPED_HELP(txt) txt +#endif + +// Each command-line flag has two variables associated with it: one +// with the current value, and one with the default value. However, +// we have a third variable, which is where value is assigned; it's a +// constant. This guarantees that FLAG_##value is initialized at +// static initialization time (e.g. before program-start) rather than +// than global construction time (which is after program-start but +// before main), at least when 'value' is a compile-time constant. We +// use a small trick for the "default value" variable, and call it +// FLAGS_no. This serves the second purpose of assuring a +// compile error if someone tries to define a flag named no +// which is illegal (--foo and --nofoo both affect the "foo" flag). +#define DEFINE_VARIABLE(type, shorttype, name, value, help) \ + namespace fL##shorttype { \ + static const type FLAGS_nono##name = value; \ + type FLAGS_##name = FLAGS_nono##name; \ + type FLAGS_no##name = FLAGS_nono##name; \ + static ::google::FlagRegisterer o_##name( \ + #name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \ + &FLAGS_##name, &FLAGS_no##name); \ + } \ + using fL##shorttype::FLAGS_##name + +#define DECLARE_VARIABLE(type, shorttype, name) \ + namespace fL##shorttype { \ + extern type FLAGS_##name; \ + } \ + using fL##shorttype::FLAGS_##name + +// For DEFINE_bool, we want to do the extra check that the passed-in +// value is actually a bool, and not a string or something that can be +// coerced to a bool. These declarations (no definition needed!) will +// help us do that, and never evaluate From, which is important. +// We'll use 'sizeof(IsBool(val))' to distinguish. This code requires +// that the compiler have different sizes for bool & double. Since +// this is not guaranteed by the standard, we check it with a +// compile-time assert (msg[-1] will give a compile-time error). +namespace fLB { +struct CompileAssert {}; +typedef CompileAssert expected_sizeof_double_neq_sizeof_bool[ + (sizeof(double) != sizeof(bool)) ? 1 : -1]; +template double IsBoolFlag(const From& from); +bool IsBoolFlag(bool from); +} // namespace fLB + +#define DECLARE_bool(name) DECLARE_VARIABLE(bool,B, name) +#define DEFINE_bool(name,val,txt) \ + namespace fLB { \ + typedef CompileAssert FLAG_##name##_value_is_not_a_bool[ \ + (sizeof(::fLB::IsBoolFlag(val)) != sizeof(double)) ? 1 : -1]; \ + } \ + DEFINE_VARIABLE(bool,B, name, val, txt) + +#define DECLARE_int32(name) DECLARE_VARIABLE(::google::int32,I, name) +#define DEFINE_int32(name,val,txt) DEFINE_VARIABLE(::google::int32,I, name, val, txt) + +#define DECLARE_int64(name) DECLARE_VARIABLE(::google::int64,I64, name) +#define DEFINE_int64(name,val,txt) DEFINE_VARIABLE(::google::int64,I64, name, val, txt) + +#define DECLARE_uint64(name) DECLARE_VARIABLE(::google::uint64,U64, name) +#define DEFINE_uint64(name,val,txt) DEFINE_VARIABLE(::google::uint64,U64, name, val, txt) + +#define DECLARE_double(name) DECLARE_VARIABLE(double,D, name) +#define DEFINE_double(name,val,txt) DEFINE_VARIABLE(double,D, name, val, txt) + +// Strings are trickier, because they're not a POD, so we can't +// construct them at static-initialization time (instead they get +// constructed at global-constructor time, which is much later). To +// try to avoid crashes in that case, we use a char buffer to store +// the string, which we can static-initialize, and then placement-new +// into it later. It's not perfect, but the best we can do. +#define DECLARE_string(name) namespace fLS { extern std::string& FLAGS_##name; } \ + using fLS::FLAGS_##name + +// We need to define a var named FLAGS_no##name so people don't define +// --string and --nostring. And we need a temporary place to put val +// so we don't have to evaluate it twice. Two great needs that go +// great together! +// The weird 'using' + 'extern' inside the fLS namespace is to work around +// an unknown compiler bug/issue with the gcc 4.2.1 on SUSE 10. See +// http://code.google.com/p/google-gflags/issues/detail?id=20 +#define DEFINE_string(name, val, txt) \ + namespace fLS { \ + static union { void* align; char s[sizeof(std::string)]; } s_##name[2]; \ + const std::string* const FLAGS_no##name = new (s_##name[0].s) std::string(val); \ + static ::google::FlagRegisterer o_##name( \ + #name, "string", MAYBE_STRIPPED_HELP(txt), __FILE__, \ + s_##name[0].s, new (s_##name[1].s) std::string(*FLAGS_no##name)); \ + extern std::string& FLAGS_##name; \ + using fLS::FLAGS_##name; \ + std::string& FLAGS_##name = *(reinterpret_cast(s_##name[0].s)); \ + } \ + using fLS::FLAGS_##name + +#endif // SWIG + +#endif // GOOGLE_GFLAGS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h new file mode 100644 index 000000000000..9d9ce7a5f754 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h @@ -0,0 +1,121 @@ +// Copyright (c) 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. +// +// --- +// Author: Dave Nicponski +// +// Implement helpful bash-style command line flag completions +// +// ** Functional API: +// HandleCommandLineCompletions() should be called early during +// program startup, but after command line flag code has been +// initialized, such as the beginning of HandleCommandLineHelpFlags(). +// It checks the value of the flag --tab_completion_word. If this +// flag is empty, nothing happens here. If it contains a string, +// however, then HandleCommandLineCompletions() will hijack the +// process, attempting to identify the intention behind this +// completion. Regardless of the outcome of this deduction, the +// process will be terminated, similar to --helpshort flag +// handling. +// +// ** Overview of Bash completions: +// Bash can be told to programatically determine completions for the +// current 'cursor word'. It does this by (in this case) invoking a +// command with some additional arguments identifying the command +// being executed, the word being completed, and the previous word +// (if any). Bash then expects a sequence of output lines to be +// printed to stdout. If these lines all contain a common prefix +// longer than the cursor word, bash will replace the cursor word +// with that common prefix, and display nothing. If there isn't such +// a common prefix, bash will display the lines in pages using 'more'. +// +// ** Strategy taken for command line completions: +// If we can deduce either the exact flag intended, or a common flag +// prefix, we'll output exactly that. Otherwise, if information +// must be displayed to the user, we'll take the opportunity to add +// some helpful information beyond just the flag name (specifically, +// we'll include the default flag value and as much of the flag's +// description as can fit on a single terminal line width, as specified +// by the flag --tab_completion_columns). Furthermore, we'll try to +// make bash order the output such that the most useful or relevent +// flags are the most likely to be shown at the top. +// +// ** Additional features: +// To assist in finding that one really useful flag, substring matching +// was implemented. Before pressing a to get completion for the +// current word, you can append one or more '?' to the flag to do +// substring matching. Here's the semantics: +// --foo Show me all flags with names prefixed by 'foo' +// --foo? Show me all flags with 'foo' somewhere in the name +// --foo?? Same as prior case, but also search in module +// definition path for 'foo' +// --foo??? Same as prior case, but also search in flag +// descriptions for 'foo' +// Finally, we'll trim the output to a relatively small number of +// flags to keep bash quiet about the verbosity of output. If one +// really wanted to see all possible matches, appending a '+' to the +// search word will force the exhaustive list of matches to be printed. +// +// ** How to have bash accept completions from a binary: +// Bash requires that it be informed about each command that programmatic +// completion should be enabled for. Example addition to a .bashrc +// file would be (your path to gflags_completions.sh file may differ): + +/* +$ complete -o bashdefault -o default -o nospace -C \ + '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \ + time env binary_name another_binary [...] +*/ + +// This would allow the following to work: +// $ /path/to/binary_name --vmodule +// Or: +// $ ./bin/path/another_binary --gfs_u +// (etc) +// +// Sadly, it appears that bash gives no easy way to force this behavior for +// all commands. That's where the "time" in the above example comes in. +// If you haven't specifically added a command to the list of completion +// supported commands, you can still get completions by prefixing the +// entire command with "env". +// $ env /some/brand/new/binary --vmod +// Assuming that "binary" is a newly compiled binary, this should still +// produce the expected completion output. + + +#ifndef GOOGLE_GFLAGS_COMPLETIONS_H_ +#define GOOGLE_GFLAGS_COMPLETIONS_H_ + +namespace google { + +void HandleCommandLineCompletions(void); + +} + +#endif // GOOGLE_GFLAGS_COMPLETIONS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h new file mode 100644 index 000000000000..17805fbadd4e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h @@ -0,0 +1,84 @@ +// Copyright (c) 2007, 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. + +#ifndef BASE_LOG_SEVERITY_H__ +#define BASE_LOG_SEVERITY_H__ + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// Variables of type LogSeverity are widely taken to lie in the range +// [0, NUM_SEVERITIES-1]. Be careful to preserve this assumption if +// you ever need to change their values or add a new severity. +typedef int LogSeverity; + +const int INFO = 0, WARNING = 1, ERROR = 2, FATAL = 3, NUM_SEVERITIES = 4; + +// DFATAL is FATAL in debug mode, ERROR in normal mode +#ifdef NDEBUG +#define DFATAL_LEVEL ERROR +#else +#define DFATAL_LEVEL FATAL +#endif + +extern GOOGLE_GLOG_DLL_DECL const char* const LogSeverityNames[NUM_SEVERITIES]; + +// NDEBUG usage helpers related to (RAW_)DCHECK: +// +// DEBUG_MODE is for small !NDEBUG uses like +// if (DEBUG_MODE) foo.CheckThatFoo(); +// instead of substantially more verbose +// #ifndef NDEBUG +// foo.CheckThatFoo(); +// #endif +// +// IF_DEBUG_MODE is for small !NDEBUG uses like +// IF_DEBUG_MODE( string error; ) +// DCHECK(Foo(&error)) << error; +// instead of substantially more verbose +// #ifndef NDEBUG +// string error; +// DCHECK(Foo(&error)) << error; +// #endif +// +#ifdef NDEBUG +enum { DEBUG_MODE = 0 }; +#define IF_DEBUG_MODE(x) +#else +enum { DEBUG_MODE = 1 }; +#define IF_DEBUG_MODE(x) x +#endif + +#endif // BASE_LOG_SEVERITY_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h new file mode 100644 index 000000000000..718c97169d84 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h @@ -0,0 +1,1499 @@ +// Copyright (c) 1999, 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. +// +// Author: Ray Sidney +// +// This file contains #include information about logging-related stuff. +// Pretty much everybody needs to #include this file so that they can +// log various happenings. +// +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include +#include +#include +#include +#if 1 +# include +#endif +#ifdef __DEPRECATED +// Make GCC quiet. +# undef __DEPRECATED +# include +# define __DEPRECATED +#else +# include +#endif +#include + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// We care a lot about number of bits things take up. Unfortunately, +// systems define their bit-specific ints in a lot of different ways. +// We use our own way, and have a typedef to get there. +// Note: these commands below may look like "#if 1" or "#if 0", but +// that's because they were constructed that way at ./configure time. +// Look at logging.h.in to see how they're calculated (based on your config). +#if 1 +#include // the normal place uint16_t is defined +#endif +#if 1 +#include // the normal place u_int16_t is defined +#endif +#if 1 +#include // a third place for uint16_t or u_int16_t +#endif + +#if 0 +#include +#endif + +namespace google { + +#if 1 // the C99 format +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#elif 1 // the BSD format +typedef int32_t int32; +typedef u_int32_t uint32; +typedef int64_t int64; +typedef u_int64_t uint64; +#elif 0 // the windows (vc7) format +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +#error Do not know how to define a 32-bit integer quantity on your system +#endif + +} + +// The global value of GOOGLE_STRIP_LOG. All the messages logged to +// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed. +// If it can be determined at compile time that the message will not be +// printed, the statement will be compiled out. +// +// Example: to strip out all INFO and WARNING messages, use the value +// of 2 below. To make an exception for WARNING messages from a single +// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including +// base/logging.h +#ifndef GOOGLE_STRIP_LOG +#define GOOGLE_STRIP_LOG 0 +#endif + +// GCC can be told that a certain branch is not likely to be taken (for +// instance, a CHECK failure), and use that information in static analysis. +// Giving it this information can help it optimize for the common case in +// the absence of better information (ie. -fprofile-arcs). +// +#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN +#if 1 +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x +#endif +#endif + +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can capture log messages in a string, rather than reporting them +// immediately: +// +// vector errors; +// LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num; +// +// This pushes back the new error onto 'errors'; if given a NULL pointer, +// it reports the error via LOG(ERROR). +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// You can also do occasional logging (log every n'th occurrence of an +// event): +// +// LOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie"; +// +// The above will cause log messages to be output on the 1st, 11th, 21st, ... +// times it is executed. Note that the special COUNTER value is used to +// identify which repetition is happening. +// +// You can also do occasional conditional logging (log every n'th +// occurrence of an event, when condition is satisfied): +// +// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << COUNTER +// << "th big cookie"; +// +// You can log messages the first N times your code executes a line. E.g. +// +// LOG_FIRST_N(INFO, 20) << "Got the " << COUNTER << "th cookie"; +// +// Outputs log messages for the first 20 times it is executed. +// +// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available. +// These log to syslog as well as to the normal logs. If you use these at +// all, you need to be aware that syslog can drastically reduce performance, +// especially if it is configured for remote logging! Don't use these +// unless you fully understand this and have a concrete need to use them. +// Even then, try to minimize your use of them. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// DLOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=mapreduce=2,file=1,gfs*=3 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc} +// b. VLOG(1) and lower messages to be printed from file.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with "gfs" +// d. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) wildcards. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level" +// condition macros for sample cases, when some extra computation and +// preparation for logs is not needed. +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// VLOG_EVERY_N(1, 10) +// << "I'm printed every 10th occurrence, and when you run the program " +// "with --v=1 or more. Present occurence is " << COUNTER; +// VLOG_IF_EVERY_N(1, (size > 1024), 10) +// << "I'm printed on every 10th occurence of case when size is more " +// " than 1024, when you run the program with --v=1 or more. "; +// "Present occurence is " << COUNTER; +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// Note that messages of a given severity are logged not only in the +// logfile for that severity, but also in all logfiles of lower severity. +// E.g., a message of severity FATAL will be logged to the logfiles of +// severity FATAL, ERROR, WARNING, and INFO. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR in normal mode. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Unless otherwise specified, logs will be written to the filename +// "...log..", followed +// by the date, time, and pid (you can't prevent the date, time, and pid +// from being in the filename). +// +// The logging code takes two flags: +// --v=# set the verbose level +// --logtostderr log all the messages to stderr instead of to logfiles + +// LOG LINE PREFIX FORMAT +// +// Log lines have this form: +// +// Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +// +// where the fields are defined as follows: +// +// L A single character, representing the log level +// (eg 'I' for INFO) +// mm The month (zero padded; ie May is '05') +// dd The day (zero padded) +// hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds +// threadid The space-padded thread ID as returned by GetTID() +// (this matches the PID on Linux) +// file The file name +// line The line number +// msg The user-supplied message +// +// Example: +// +// I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog +// I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395 +// +// NOTE: although the microseconds are useful for comparing events on +// a single machine, clocks on different machines may not be well +// synchronized. Hence, use caution when comparing the low bits of +// timestamps from different machines. + +#ifndef DECLARE_VARIABLE +#define MUST_UNDEF_GFLAGS_DECLARE_MACROS +#define DECLARE_VARIABLE(type, name, tn) \ + namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##tn##_instead { \ + extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name; \ + } \ + using FLAG__namespace_do_not_use_directly_use_DECLARE_##tn##_instead::FLAGS_##name + +// bool specialization +#define DECLARE_bool(name) \ + DECLARE_VARIABLE(bool, name, bool) + +// int32 specialization +#define DECLARE_int32(name) \ + DECLARE_VARIABLE(google::int32, name, int32) + +// Special case for string, because we have to specify the namespace +// std::string, which doesn't play nicely with our FLAG__namespace hackery. +#define DECLARE_string(name) \ + namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead { \ + extern GOOGLE_GLOG_DLL_DECL std::string FLAGS_##name; \ + } \ + using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name +#endif + +// Set whether log messages go to stderr instead of logfiles +DECLARE_bool(logtostderr); + +// Set whether log messages go to stderr in addition to logfiles. +DECLARE_bool(alsologtostderr); + +// Log messages at a level >= this flag are automatically sent to +// stderr in addition to log files. +DECLARE_int32(stderrthreshold); + +// Set whether the log prefix should be prepended to each line of output. +DECLARE_bool(log_prefix); + +// Log messages at a level <= this flag are buffered. +// Log messages at a higher level are flushed immediately. +DECLARE_int32(logbuflevel); + +// Sets the maximum number of seconds which logs may be buffered for. +DECLARE_int32(logbufsecs); + +// Log suppression level: messages logged at a lower level than this +// are suppressed. +DECLARE_int32(minloglevel); + +// If specified, logfiles are written into this directory instead of the +// default logging directory. +DECLARE_string(log_dir); + +// Sets the path of the directory into which to put additional links +// to the log files. +DECLARE_string(log_link); + +DECLARE_int32(v); // in vlog_is_on.cc + +// Sets the maximum log file size (in MB). +DECLARE_int32(max_log_size); + +// Sets whether to avoid logging to the disk if the disk is full. +DECLARE_bool(stop_logging_if_full_disk); + +#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef DECLARE_VARIABLE +#undef DECLARE_bool +#undef DECLARE_int32 +#undef DECLARE_string +#endif + +// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for +// security reasons. See LOG(severtiy) below. + +// A few definitions of macros that don't generate much code. Since +// LOG(INFO) and its ilk are used all over our code, it's +// better to have compact code for these operations. + +#if GOOGLE_STRIP_LOG == 0 +#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_INFO(message) google::LogMessage( \ + __FILE__, __LINE__, google::INFO, message) +#else +#define COMPACT_GOOGLE_LOG_INFO google::NullStream() +#define LOG_TO_STRING_INFO(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 1 +#define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \ + __FILE__, __LINE__, google::WARNING) +#define LOG_TO_STRING_WARNING(message) google::LogMessage( \ + __FILE__, __LINE__, google::WARNING, message) +#else +#define COMPACT_GOOGLE_LOG_WARNING google::NullStream() +#define LOG_TO_STRING_WARNING(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 2 +#define COMPACT_GOOGLE_LOG_ERROR google::LogMessage( \ + __FILE__, __LINE__, google::ERROR) +#define LOG_TO_STRING_ERROR(message) google::LogMessage( \ + __FILE__, __LINE__, google::ERROR, message) +#else +#define COMPACT_GOOGLE_LOG_ERROR google::NullStream() +#define LOG_TO_STRING_ERROR(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_FATAL(message) google::LogMessage( \ + __FILE__, __LINE__, google::FATAL, message) +#else +#define COMPACT_GOOGLE_LOG_FATAL google::NullStreamFatal() +#define LOG_TO_STRING_FATAL(message) google::NullStreamFatal() +#endif + +// For DFATAL, we want to use LogMessage (as opposed to +// LogMessageFatal), to be consistent with the original behavior. +#ifdef NDEBUG +#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR +#elif GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_DFATAL LogMessage( \ + __FILE__, __LINE__, google::FATAL) +#else +#define COMPACT_GOOGLE_LOG_DFATAL google::NullStreamFatal() +#endif + +#define GOOGLE_LOG_INFO(counter) google::LogMessage(__FILE__, __LINE__, google::INFO, counter, &google::LogMessage::SendToLog) +#define SYSLOG_INFO(counter) \ + google::LogMessage(__FILE__, __LINE__, google::INFO, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::WARNING, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::WARNING, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::ERROR, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::ERROR, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::FATAL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::FATAL, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToSyslogAndLog) + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) +// A very useful logging macro to log windows errors: +#define LOG_SYSRESULT(result) \ + if (FAILED(result)) { \ + LPTSTR message = NULL; \ + LPTSTR msg = reinterpret_cast(&message); \ + DWORD message_length = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM, \ + 0, result, 0, msg, 100, NULL); \ + if (message_length > 0) { \ + google::LogMessage(__FILE__, __LINE__, ERROR, 0, \ + &google::LogMessage::SendToLog).stream() << message; \ + LocalFree(message); \ + } \ + } +#endif + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() +#define SYSLOG(severity) SYSLOG_ ## severity(0).stream() + +namespace google { + +// They need the definitions of integer types. +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Initialize google's logging library. You will see the program name +// specified by argv0 in log outputs. +GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0); + +// Install a function which will be called after LOG(FATAL). +GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); + +class LogSink; // defined below + +// If a non-NULL sink pointer is given, we push this message to that sink. +// For LOG_TO_SINK we then do normal LOG(severity) logging as well. +// This is useful for capturing messages and passing/storing them +// somewhere more specific than the global log of the process. +// Argument types: +// LogSink* sink; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +#define LOG_TO_SINK(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::severity, \ + static_cast(sink), true).stream() +#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::severity, \ + static_cast(sink), false).stream() + +// If a non-NULL string pointer is given, we write this message to that string. +// We then do normal LOG(severity) logging as well. +// This is useful for capturing messages and storing them somewhere more +// specific than the global log of the process. +// Argument types: +// string* message; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +// NOTE: LOG(severity) expands to LogMessage().stream() for the specified +// severity. +#define LOG_TO_STRING(severity, message) \ + LOG_TO_STRING_##severity(static_cast(message)).stream() + +// If a non-NULL pointer is given, we push the message onto the end +// of a vector of strings; otherwise, we report it with LOG(severity). +// This is handy for capturing messages and perhaps passing them back +// to the caller, rather than reporting them immediately. +// Argument types: +// LogSeverity severity; +// vector *outvec; +// The cast is to disambiguate NULL arguments. +#define LOG_STRING(severity, outvec) \ + LOG_TO_STRING_##severity(static_cast*>(outvec)).stream() + +#define LOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) +#define SYSLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG, so the check will be executed regardless of +// compilation mode. Therefore, it is safe to do things like: +// CHECK(fp->Write(x) == 4) +#define CHECK(condition) \ + LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A container for a string pointer which can be evaluated to a bool - +// true iff the pointer is NULL. +struct CheckOpString { + CheckOpString(std::string* str) : str_(str) { } + // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), + // so there's no point in cleaning up str_. + operator bool() const { + return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL); + } + std::string* str_; +}; + +// Function is overloaded for integral types to allow static const +// integrals declared in classes and not defined to be used as arguments to +// CHECK* macros. It's not encouraged though. +template +inline const T& GetReferenceableValue(const T& t) { return t; } +inline char GetReferenceableValue(char t) { return t; } +inline unsigned char GetReferenceableValue(unsigned char t) { return t; } +inline signed char GetReferenceableValue(signed char t) { return t; } +inline short GetReferenceableValue(short t) { return t; } +inline unsigned short GetReferenceableValue(unsigned short t) { return t; } +inline int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline long GetReferenceableValue(long t) { return t; } +inline unsigned long GetReferenceableValue(unsigned long t) { return t; } +inline long long GetReferenceableValue(long long t) { return t; } +inline unsigned long long GetReferenceableValue(unsigned long long t) { + return t; +} + +// This is a dummy class to define the following operator. +struct DummyClassToDefineOperator {}; + +} + +// Define global operator<< to declare using ::operator<<. +// This declaration will allow use to use CHECK macros for user +// defined classes which have operator<< (e.g., stl_logging.h). +inline std::ostream& operator<<( + std::ostream& out, const google::DummyClassToDefineOperator&) { + return out; +} + +namespace google { + +// Build the error message string. +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + // It means that we cannot use stl_logging if compiler doesn't + // support using expression for operator. + // TODO(hamaji): Figure out a way to fix. +#if 1 + using ::operator<<; +#endif + std::strstream ss; + ss << names << " (" << v1 << " vs. " << v2 << ")"; + return new std::string(ss.str(), ss.pcount()); +} + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + return Check##name##Impl(v1, v2, names); \ + } + +// Use _EQ, _NE, _LE, etc. in case the file including base/logging.h +// provides its own #defines for the simpler names EQ, NE, LE, etc. +// This happens if, for example, those are used as token names in a +// yacc grammar. +DEFINE_CHECK_OP_IMPL(_EQ, ==) +DEFINE_CHECK_OP_IMPL(_NE, !=) +DEFINE_CHECK_OP_IMPL(_LE, <=) +DEFINE_CHECK_OP_IMPL(_LT, < ) +DEFINE_CHECK_OP_IMPL(_GE, >=) +DEFINE_CHECK_OP_IMPL(_GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. + +#if defined(STATIC_ANALYSIS) +// Only for static analysis tool to know that it is equivalent to assert +#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2)) +#elif !defined(NDEBUG) +// In debug mode, avoid constructing CheckOpStrings if possible, +// to reduce the overhead of CHECK statments by 2x. +// Real DCHECK-heavy tests have seen 1.5x speedups. + +// The meaning of "string" might be different between now and +// when this macro gets invoked (e.g., if someone is experimenting +// with other string implementations that get defined after this +// file is included). Save the current meaning now and use it +// in the macro. +typedef std::string _Check_string; +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::_Check_string* _result = \ + google::Check##name##Impl( \ + google::GetReferenceableValue(val1), \ + google::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, \ + google::CheckOpString(_result)).stream() +#else +// In optimized mode, use CheckOpString to hint to compiler that +// the while condition is unlikely. +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::CheckOpString _result = \ + google::Check##name##Impl(GetReferenceableValue(val1), \ + GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, _result).stream() +#endif // STATIC_ANALYSIS, !NDEBUG + +#if GOOGLE_STRIP_LOG <= 3 +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal) +#else +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::NullStreamFatal) +#endif // STRIP_LOG <= 3 + +// Equality/Inequality checks - compare two values, and log a FATAL message +// including the two values when the result is not as expected. The values +// must have operator<<(ostream, ...) defined. +// +// You may append to the error message like so: +// CHECK_NE(1, 2) << ": The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// CHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is NULL. To work around this, simply static_cast NULL to the +// type of the desired pointer. + +#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) + +// Check that the input is non NULL. This very useful in constructor +// initializer lists. + +#define CHECK_NOTNULL(val) \ + google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) + +// Helper functions for string comparisons. +// To avoid bloat, the definitions are in logging.cc. +#define DECLARE_CHECK_STROP_IMPL(func, expected) \ + GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \ + const char* s1, const char* s2, const char* names); +DECLARE_CHECK_STROP_IMPL(strcmp, true) +DECLARE_CHECK_STROP_IMPL(strcmp, false) +DECLARE_CHECK_STROP_IMPL(strcasecmp, true) +DECLARE_CHECK_STROP_IMPL(strcasecmp, false) +#undef DECLARE_CHECK_STROP_IMPL + +// Helper macro for string comparisons. +// Don't use this macro directly in your code, use CHECK_STREQ et al below. +#define CHECK_STROP(func, op, expected, s1, s2) \ + while (google::CheckOpString _result = \ + google::Check##func##expected##Impl((s1), (s2), \ + #s1 " " #op " " #s2)) \ + LOG(FATAL) << *_result.str_ + + +// String (char*) equality/inequality checks. +// CASE versions are case-insensitive. +// +// Note that "s1" and "s2" may be temporary strings which are destroyed +// by the compiler at the end of the current "full expression" +// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). + +#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) +#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) +#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2) +#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2) + +#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0]))) +#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0]))) + +#define CHECK_DOUBLE_EQ(val1, val2) \ + do { \ + CHECK_LE((val1), (val2)+0.000000000000001L); \ + CHECK_GE((val1), (val2)-0.000000000000001L); \ + } while (0) + +#define CHECK_NEAR(val1, val2, margin) \ + do { \ + CHECK_LE((val1), (val2)+(margin)); \ + CHECK_GE((val1), (val2)-(margin)); \ + } while (0) + +// perror()..googly style! +// +// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and +// CHECK equivalents with the addition that they postpend a description +// of the current state of errno to their output lines. + +#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream() + +#define GOOGLE_PLOG(severity, counter) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::severity, counter, \ + &google::LogMessage::SendToLog) + +#define PLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity) + +// A CHECK() macro that postpends errno if the condition is false. E.g. +// +// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... } +#define PCHECK(condition) \ + PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A CHECK() macro that lets you assert the success of a function that +// returns -1 and sets errno in case of an error. E.g. +// +// CHECK_ERR(mkdir(path, 0700)); +// +// or +// +// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename; +#define CHECK_ERR(invocation) \ +PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1)) \ + << #invocation + +// Use macro expansion to create, for each use of LOG_EVERY_N(), static +// variables with the __LINE__ expansion as part of the variable name. +#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line) +#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line + +#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__) +#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__) + +#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::LogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (condition && \ + ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \ + google::LogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0; \ + if (LOG_OCCURRENCES <= n) \ + ++LOG_OCCURRENCES; \ + if (LOG_OCCURRENCES <= n) \ + google::LogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +namespace glog_internal_namespace_ { +template +struct CompileAssert { +}; +struct CrashReason; +} // namespace glog_internal_namespace_ + +#define GOOGLE_GLOG_COMPILE_ASSERT(expr, msg) \ + typedef google::glog_internal_namespace_::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +#define LOG_EVERY_N(severity, n) \ + GOOGLE_GLOG_COMPILE_ASSERT(google::severity < \ + google::NUM_SEVERITIES, \ + INVALID_REQUESTED_LOG_SEVERITY); \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define SYSLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToSyslogAndLog) + +#define PLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_PLOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_FIRST_N(severity, n) \ + SOME_KIND_OF_LOG_FIRST_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_IF_EVERY_N(severity, condition, n) \ + SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), google::LogMessage::SendToLog) + +// We want the special COUNTER value available for LOG_EVERY_X()'ed messages +enum PRIVATE_Counter {COUNTER}; + + +// Plus some debug-logging macros that get compiled to nothing for production + +#ifndef NDEBUG + +#define DLOG(severity) LOG(severity) +#define DVLOG(verboselevel) VLOG(verboselevel) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n) +#define DLOG_IF_EVERY_N(severity, condition, n) \ + LOG_IF_EVERY_N(severity, condition, n) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) + +// debug-only checking. not executed in NDEBUG mode. +#define DCHECK(condition) CHECK(condition) +#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) +#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) +#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) +#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) +#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) +#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) +#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2) +#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2) +#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2) +#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2) + +#else // NDEBUG + +#define DLOG(severity) \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DVLOG(verboselevel) \ + (true || !VLOG_IS_ON(verboselevel)) ?\ + (void) 0 : google::LogMessageVoidify() & LOG(INFO) + +#define DLOG_IF(severity, condition) \ + (true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_EVERY_N(severity, n) \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_IF_EVERY_N(severity, condition, n) \ + (true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_ASSERT(condition) \ + true ? (void) 0 : LOG_ASSERT(condition) + +#define DCHECK(condition) \ + while (false) \ + CHECK(condition) + +#define DCHECK_EQ(val1, val2) \ + while (false) \ + CHECK_EQ(val1, val2) + +#define DCHECK_NE(val1, val2) \ + while (false) \ + CHECK_NE(val1, val2) + +#define DCHECK_LE(val1, val2) \ + while (false) \ + CHECK_LE(val1, val2) + +#define DCHECK_LT(val1, val2) \ + while (false) \ + CHECK_LT(val1, val2) + +#define DCHECK_GE(val1, val2) \ + while (false) \ + CHECK_GE(val1, val2) + +#define DCHECK_GT(val1, val2) \ + while (false) \ + CHECK_GT(val1, val2) + +#define DCHECK_STREQ(str1, str2) \ + while (false) \ + CHECK_STREQ(str1, str2) + +#define DCHECK_STRCASEEQ(str1, str2) \ + while (false) \ + CHECK_STRCASEEQ(str1, str2) + +#define DCHECK_STRNE(str1, str2) \ + while (false) \ + CHECK_STRNE(str1, str2) + +#define DCHECK_STRCASENE(str1, str2) \ + while (false) \ + CHECK_STRCASENE(str1, str2) + + +#endif // NDEBUG + +// Log only in verbose mode. + +#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) + +#define VLOG_IF(verboselevel, condition) \ + LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel)) + +#define VLOG_EVERY_N(verboselevel, n) \ + LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n) + +#define VLOG_IF_EVERY_N(verboselevel, condition, n) \ + LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n) + +// +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class GOOGLE_GLOG_DLL_DECL LogMessage { +public: + enum { + // Passing kNoLogPrefix for the line number disables the + // log-message prefix. Useful for using the LogMessage + // infrastructure as a printing utility. See also the --log_prefix + // flag for controlling the log-message prefix on an + // application-wide basis. + kNoLogPrefix = -1 + }; + + // LogStream inherit from non-DLL-exported class (std::ostrstream) + // and VC++ produces a warning for this situation. + // However, MSDN says "C4275 can be ignored in Microsoft Visual C++ + // 2005 if you are deriving from a type in the Standard C++ Library" + // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx + // Let's just ignore the warning. +#ifdef _MSC_VER +# pragma warning(disable: 4275) +#endif + class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostrstream { +#ifdef _MSC_VER +# pragma warning(default: 4275) +#endif + public: + LogStream(char *buf, int len, int ctr) + : ostrstream(buf, len), + ctr_(ctr) { + self_ = this; + } + + int ctr() const { return ctr_; } + void set_ctr(int ctr) { ctr_ = ctr; } + LogStream* self() const { return self_; } + + private: + int ctr_; // Counter hack (for the LOG_EVERY_X() macro) + LogStream *self_; // Consistency check hack + }; + +public: + // icc 8 requires this typedef to avoid an internal compiler error. + typedef void (LogMessage::*SendMethod)(); + + LogMessage(const char* file, int line, LogSeverity severity, int ctr, + SendMethod send_method); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + + // Used for LOG(INFO): Implied are: + // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog. + // + // Using this constructor instead of the more complex constructor above + // saves 19 bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0, send_method = &LogMessage::SendToLog + // + // Using this constructor instead of the more complex constructor above + // saves 17 bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // Constructor to log this message to a specified sink (if not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if + // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise. + LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink, + bool also_send_to_log); + + // Constructor where we also give a vector pointer + // for storing the messages (if the pointer is not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::vector* outvec); + + // Constructor where we also give a string pointer for storing the + // message (if the pointer is not NULL). Implied are: ctr = 0, + // send_method = &LogMessage::WriteToStringAndLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* message); + + // A special constructor used for check failures + LogMessage(const char* file, int line, const CheckOpString& result); + + ~LogMessage(); + + // Flush a buffered message to the sink set in the constructor. Always + // called by the destructor, it may also be called from elsewhere if + // needed. Only the first call is actioned; any later ones are ignored. + void Flush(); + + // An arbitrary limit on the length of a single log message. This + // is so that streaming can be done more efficiently. + static const size_t kMaxLogMessageLen; + + // Theses should not be called directly outside of logging.*, + // only passed as SendMethod arguments to other LogMessage methods: + void SendToLog(); // Actually dispatch to the logs + void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs + + // Call abort() or similar to perform LOG(FATAL) crash. + static void Fail() __attribute__ ((noreturn)); + + std::ostream& stream() { return *(data_->stream_); } + + int preserved_errno() const { return data_->preserved_errno_; } + + // Must be called without the log_mutex held. (L < log_mutex) + static int64 num_messages(int severity); + +private: + // Fully internal SendMethod cases: + void SendToSinkAndLog(); // Send to sink if provided and dispatch to the logs + void SendToSink(); // Send to sink if provided, do nothing otherwise. + + // Write to string if provided and dispatch to the logs. + void WriteToStringAndLog(); + + void SaveOrSendToLog(); // Save to stringvec if provided, else to logs + + void Init(const char* file, int line, LogSeverity severity, + void (LogMessage::*send_method)()); + + // Used to fill in crash information during LOG(FATAL) failures. + void RecordCrashReason(glog_internal_namespace_::CrashReason* reason); + + // Counts of messages sent at each priority: + static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex + + // We keep the data in a separate struct so that each instance of + // LogMessage uses less stack space. + struct GOOGLE_GLOG_DLL_DECL LogMessageData { + LogMessageData() {}; + + int preserved_errno_; // preserved errno + char* buf_; + char* message_text_; // Complete message text (points to selected buffer) + LogStream* stream_alloc_; + LogStream* stream_; + char severity_; // What level is this LogMessage logged at? + int line_; // line number where logging call is. + void (LogMessage::*send_method_)(); // Call this in destructor to send + union { // At most one of these is used: union to keep the size low. + LogSink* sink_; // NULL or sink to send message to + std::vector* outvec_; // NULL or vector to push message onto + std::string* message_; // NULL or string to write message into + }; + time_t timestamp_; // Time of creation of LogMessage + struct ::tm tm_time_; // Time of creation of LogMessage + size_t num_prefix_chars_; // # of chars of prefix in this message + size_t num_chars_to_log_; // # of chars of msg to send to log + size_t num_chars_to_syslog_; // # of chars of msg to send to syslog + const char* basename_; // basename of file that called LOG + const char* fullname_; // fullname of file that called LOG + bool has_been_flushed_; // false => data has not been flushed + bool first_fatal_; // true => this was first fatal msg + + ~LogMessageData(); + private: + LogMessageData(const LogMessageData&); + void operator=(const LogMessageData&); + }; + + static LogMessageData fatal_msg_data_exclusive_; + static LogMessageData fatal_msg_data_shared_; + + LogMessageData* allocated_; + LogMessageData* data_; + + friend class LogDestination; + + LogMessage(const LogMessage&); + void operator=(const LogMessage&); +}; + +// This class happens to be thread-hostile because all instances share +// a single data buffer, but since it can only be created just before +// the process dies, we don't worry so much. +class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage { + public: + LogMessageFatal(const char* file, int line); + LogMessageFatal(const char* file, int line, const CheckOpString& result); + ~LogMessageFatal() __attribute__ ((noreturn)); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(int const severity, std::string const &msg) { + LogMessage(__FILE__, __LINE__, severity).stream() << msg; +} + +// A macro alternative of LogAtLevel. New code may want to use this +// version since there are two advantages: 1. this version outputs the +// file name and the line number where this macro is put like other +// LOG macros, 2. this macro can be used as C++ stream. +#define LOG_AT_LEVEL(severity) LogMessage(__FILE__, __LINE__, severity).stream() + +// A small helper for CHECK_NOTNULL(). +template +T* CheckNotNull(const char *file, int line, const char *names, T* t) { + if (t == NULL) { + LogMessageFatal(file, line, new std::string(names)); + } + return t; +} + +// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This +// only works if ostream is a LogStream. If the ostream is not a +// LogStream you'll get an assert saying as much at runtime. +GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os, + const PRIVATE_Counter&); + + +// Derived class for PLOG*() above. +class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage { + public: + + ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr, + void (LogMessage::*send_method)()); + + // Postpends ": strerror(errno) [errno]". + ~ErrnoLogMessage(); + + private: + ErrnoLogMessage(const ErrnoLogMessage&); + void operator=(const ErrnoLogMessage&); +}; + + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". + +class GOOGLE_GLOG_DLL_DECL LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-safe. +GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity); + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-hostile because it ignores +// locking -- used for catastrophic failures. +GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity); + +// +// Set the destination to which a particular severity level of log +// messages is sent. If base_filename is "", it means "don't log this +// severity". Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity, + const char* base_filename); + +// +// Set the basename of the symlink to the latest log file at a given +// severity. If symlink_basename is empty, do not make a symlink. If +// you don't call this function, the symlink basename is the +// invocation name of the program. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity, + const char* symlink_basename); + +// +// Used to send logs to some other kind of destination +// Users should subclass LogSink and override send to do whatever they want. +// Implementations must be thread-safe because a shared instance will +// be called from whichever thread ran the LOG(XXX) line. +class GOOGLE_GLOG_DLL_DECL LogSink { + public: + virtual ~LogSink(); + + // Sink's logging logic (message_len is such as to exclude '\n' at the end). + // This method can't use LOG() or CHECK() as logging system mutex(s) are held + // during this call. + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) = 0; + + // Redefine this to implement waiting for + // the sink's logging logic to complete. + // It will be called after each send() returns, + // but before that LogMessage exits or crashes. + // By default this function does nothing. + // Using this function one can implement complex logic for send() + // that itself involves logging; and do all this w/o causing deadlocks and + // inconsistent rearrangement of log messages. + // E.g. if a LogSink has thread-specific actions, the send() method + // can simply add the message to a queue and wake up another thread that + // handles real logging while itself making some LOG() calls; + // WaitTillSent() can be implemented to wait for that logic to complete. + // See our unittest for an example. + virtual void WaitTillSent(); + + // Returns the normal text output of the log message. + // Can be useful to implement send(). + static std::string ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len); +}; + +// Add or remove a LogSink as a consumer of logging data. Thread-safe. +GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination); +GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination); + +// +// Specify an "extension" added to the filename specified via +// SetLogDestination. This applies to all severity levels. It's +// often used to append the port we're listening on to the logfile +// name. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension( + const char* filename_extension); + +// +// Make it so that all log messages of at least a particular severity +// are logged to stderr (in addition to logging to the usual log +// file(s)). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity); + +// +// Make it so that all log messages go only to stderr. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void LogToStderr(); + +// +// Make it so that all log messages of at least a particular severity are +// logged via email to a list of addresses (in addition to logging to the +// usual log file(s)). The list of addresses is just a string containing +// the email addresses to send to (separated by spaces, say). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity, + const char* addresses); + +// A simple function that sends email. dest is a commma-separated +// list of addressess. Thread-safe. +GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest, + const char *subject, const char *body); + +GOOGLE_GLOG_DLL_DECL const std::vector& GetLoggingDirectories(); + +// For tests only: Clear the internal [cached] list of logging directories to +// force a refresh the next time GetLoggingDirectories is called. +// Thread-hostile. +void TestOnly_ClearLoggingDirectoriesList(); + +// Returns a set of existing temporary directories, which will be a +// subset of the directories returned by GetLogginDirectories(). +// Thread-safe. +GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories( + std::vector* list); + +// Print any fatal message again -- useful to call from signal handler +// so that the last thing in the output is the fatal message. +// Thread-hostile, but a race is unlikely. +GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage(); + +// Truncate a log file that may be the append-only output of multiple +// processes and hence can't simply be renamed/reopened (typically a +// stdout/stderr). If the file "path" is > "limit" bytes, copy the +// last "keep" bytes to offset 0 and truncate the rest. Since we could +// be racing with other writers, this approach has the potential to +// lose very small amounts of data. For security, only follow symlinks +// if the path is /proc/self/fd/* +GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path, + int64 limit, int64 keep); + +// Truncate stdout and stderr if they are over the value specified by +// --max_log_size; keep the final 1MB. This function has the same +// race condition as TruncateLogFile. +GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr(); + +// Return the string representation of the provided LogSeverity level. +// Thread-safe. +GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity); + +// --------------------------------------------------------------------- +// Implementation details that are not useful to most clients +// --------------------------------------------------------------------- + +// A Logger is the interface used by logging modules to emit entries +// to a log. A typical implementation will dump formatted data to a +// sequence of files. We also provide interfaces that will forward +// the data to another thread so that the invoker never blocks. +// Implementations should be thread-safe since the logging system +// will write to them from multiple threads. + +namespace base { + +class GOOGLE_GLOG_DLL_DECL Logger { + public: + virtual ~Logger(); + + // Writes "message[0,message_len-1]" corresponding to an event that + // occurred at "timestamp". If "force_flush" is true, the log file + // is flushed immediately. + // + // The input message has already been formatted as deemed + // appropriate by the higher level logging facility. For example, + // textual log messages already contain timestamps, and the + // file:linenumber header. + virtual void Write(bool force_flush, + time_t timestamp, + const char* message, + int message_len) = 0; + + // Flush any buffered messages + virtual void Flush() = 0; + + // Get the current LOG file size. + // The returned value is approximate since some + // logged data may not have been flushed to disk yet. + virtual uint32 LogSize() = 0; +}; + +// Get the logger for the specified severity level. The logger +// remains the property of the logging module and should not be +// deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level); + +// Set the logger for the specified severity level. The logger +// becomes the property of the logging module and should not +// be deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger); + +} + +// glibc has traditionally implemented two incompatible versions of +// strerror_r(). There is a poorly defined convention for picking the +// version that we want, but it is not clear whether it even works with +// all versions of glibc. +// So, instead, we provide this wrapper that automatically detects the +// version that is in use, and then implements POSIX semantics. +// N.B. In addition to what POSIX says, we also guarantee that "buf" will +// be set to an empty string, if this function failed. This means, in most +// cases, you do not need to check the error code and you can directly +// use the value of "buf". It will never have an undefined value. +GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len); + + +// A class for which we define operator<<, which does nothing. +class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream { + public: + // Initialize the LogStream so the messages can be written somewhere + // (they'll never be actually displayed). This will be needed if a + // NullStream& is implicitly converted to LogStream&, in which case + // the overloaded NullStream::operator<< will not be invoked. + NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream(const char* /*file*/, int /*line*/, + const CheckOpString& /*result*/) : + LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream &stream() { return *this; } + private: + // A very short buffer for messages (which we discard anyway). This + // will be needed if NullStream& converted to LogStream& (e.g. as a + // result of a conditional expression). + char message_buffer_[2]; +}; + +// Do nothing. This operator is inline, allowing the message to be +// compiled away. The message will not be compiled away if we do +// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when +// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly +// converted to LogStream and the message will be computed and then +// quietly discarded. +template +inline NullStream& operator<<(NullStream &str, const T &value) { return str; } + +// Similar to NullStream, but aborts the program (without stack +// trace), like LogMessageFatal. +class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream { + public: + NullStreamFatal() { } + NullStreamFatal(const char* file, int line, const CheckOpString& result) : + NullStream(file, line, result) { } + __attribute__ ((noreturn)) ~NullStreamFatal() { _exit(1); } +}; + +// Install a signal handler that will dump signal information and a stack +// trace when the program crashes on certain signals. We'll install the +// signal handler for the following signals. +// +// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM. +// +// By default, the signal handler will write the failure dump to the +// standard error. You can customize the destination by installing your +// own writer function by InstallFailureWriter() below. +// +// Note on threading: +// +// The function should be called before threads are created, if you want +// to use the failure signal handler for all threads. The stack trace +// will be shown only for the thread that receives the signal. In other +// words, stack traces of other threads won't be shown. +GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler(); + +// Installs a function that is used for writing the failure dump. "data" +// is the pointer to the beginning of a message to be written, and "size" +// is the size of the message. You should not expect the data is +// terminated with '\0'. +GOOGLE_GLOG_DLL_DECL void InstallFailureWriter( + void (*writer)(const char* data, int size)); + +} + +#endif // _LOGGING_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h new file mode 100644 index 000000000000..9e9b3772f3bc --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h @@ -0,0 +1,185 @@ +// Copyright (c) 2006, 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. +// +// Author: Maxim Lifantsev +// +// Thread-safe logging routines that do not allocate any memory or +// acquire any locks, and can therefore be used by low-level memory +// allocation and synchronization code. + +#ifndef BASE_RAW_LOGGING_H_ +#define BASE_RAW_LOGGING_H_ + +#include + +namespace google { + +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// This is similar to LOG(severity) << format... and VLOG(level) << format.., +// but +// * it is to be used ONLY by low-level modules that can't use normal LOG() +// * it is desiged to be a low-level logger that does not allocate any +// memory and does not need any locks, hence: +// * it logs straight and ONLY to STDERR w/o buffering +// * it uses an explicit format and arguments list +// * it will silently chop off really long message strings +// Usage example: +// RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); +// RAW_VLOG(3, "status is %i", status); +// These will print an almost standard log lines like this to stderr only: +// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file +// I0821 211317 file.cc:142] RAW: status is 20 +#define RAW_LOG(severity, ...) \ + do { \ + switch (google::severity) { \ + case 0: \ + RAW_LOG_INFO(__VA_ARGS__); \ + break; \ + case 1: \ + RAW_LOG_WARNING(__VA_ARGS__); \ + break; \ + case 2: \ + RAW_LOG_ERROR(__VA_ARGS__); \ + break; \ + case 3: \ + RAW_LOG_FATAL(__VA_ARGS__); \ + break; \ + default: \ + break; \ + } \ + } while (0) + +// The following STRIP_LOG testing is performed in the header file so that it's +// possible to completely compile out the logging code and the log messages. +#if STRIP_LOG == 0 +#define RAW_VLOG(verboselevel, ...) \ + do { \ + if (VLOG_IS_ON(verboselevel)) { \ + RAW_LOG_INFO(__VA_ARGS__); \ + } \ + } while (0) +#else +#define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG == 0 +#define RAW_LOG_INFO(...) google::RawLog__(google::INFO, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_INFO(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG <= 1 +#define RAW_LOG_WARNING(...) google::RawLog__(google::WARNING, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_WARNING(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 1 + +#if STRIP_LOG <= 2 +#define RAW_LOG_ERROR(...) google::RawLog__(google::ERROR, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_ERROR(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 2 + +#if STRIP_LOG <= 3 +#define RAW_LOG_FATAL(...) google::RawLog__(google::FATAL, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_FATAL(...) \ + do { \ + google::RawLogStub__(0, __VA_ARGS__); \ + exit(1); \ + } while (0) +#endif // STRIP_LOG <= 3 + +// Similar to CHECK(condition) << message, +// but for low-level modules: we use only RAW_LOG that does not allocate memory. +// We do not want to provide args list here to encourage this usage: +// if (!cond) RAW_LOG(FATAL, "foo ...", hard_to_compute_args); +// so that the args are not computed when not needed. +#define RAW_CHECK(condition, message) \ + do { \ + if (!(condition)) { \ + RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ + } \ + } while (0) + +// Debug versions of RAW_LOG and RAW_CHECK +#ifndef NDEBUG + +#define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message) + +#else // NDEBUG + +#define RAW_DLOG(severity, ...) \ + while (false) \ + RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) \ + while (false) \ + RAW_CHECK(condition, message) + +#endif // NDEBUG + +// Stub log function used to work around for unused variable warnings when +// building with STRIP_LOG > 0. +static inline void RawLogStub__(int ignored, ...) { +} + +// Helper function to implement RAW_LOG and RAW_VLOG +// Logs format... at "severity" level, reporting it +// as called from file:line. +// This does not allocate memory or acquire locks. +GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity, + const char* file, + int line, + const char* format, ...) + __attribute__((__format__ (__printf__, 4, 5))); + +// Hack to propagate time information into this module so that +// this module does not have to directly call localtime_r(), +// which could allocate memory. +GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs); + +} + +#endif // BASE_RAW_LOGGING_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h new file mode 100644 index 000000000000..42f256065f67 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h @@ -0,0 +1,154 @@ +// Copyright (c) 2003, 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. +// +// Stream output operators for STL containers; to be used for logging *only*. +// Inclusion of this file lets you do: +// +// list x; +// LOG(INFO) << "data: " << x; +// vector v1, v2; +// CHECK_EQ(v1, v2); +// +// Note that if you want to use these operators from the non-global namespace, +// you may get an error since they are not in namespace std (and they are not +// in namespace std since that would result in undefined behavior). You may +// need to write +// +// using ::operator<<; +// +// to fix these errors. + +#ifndef UTIL_GTL_STL_LOGGING_INL_H_ +#define UTIL_GTL_STL_LOGGING_INL_H_ + +#if !1 +# error We do not support stl_logging for this compiler +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +# include +# include +# include +#endif + +template +inline std::ostream& operator<<(std::ostream& out, + const std::pair& p) { + out << '(' << p.first << ", " << p.second << ')'; + return out; +} + +namespace google { + +template +inline void PrintSequence(std::ostream& out, Iter begin, Iter end) { + using ::operator<<; + // Output at most 100 elements -- appropriate if used for logging. + for (int i = 0; begin != end && i < 100; ++i, ++begin) { + if (i > 0) out << ' '; + out << *begin; + } + if (begin != end) { + out << " ..."; + } +} + +} + +#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_TWO_ARG_CONTAINER(std::vector) +OUTPUT_TWO_ARG_CONTAINER(std::deque) +OUTPUT_TWO_ARG_CONTAINER(std::list) +#ifdef __GNUC__ +OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist) +#endif + +#undef OUTPUT_TWO_ARG_CONTAINER + +#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_THREE_ARG_CONTAINER(std::set) +OUTPUT_THREE_ARG_CONTAINER(std::multiset) + +#undef OUTPUT_THREE_ARG_CONTAINER + +#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_FOUR_ARG_CONTAINER(std::map) +OUTPUT_FOUR_ARG_CONTAINER(std::multimap) +#ifdef __GNUC__ +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set) +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset) +#endif + +#undef OUTPUT_FOUR_ARG_CONTAINER + +#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +#ifdef __GNUC__ +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map) +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap) +#endif + +#undef OUTPUT_FIVE_ARG_CONTAINER + +#endif // UTIL_GTL_STL_LOGGING_INL_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h new file mode 100644 index 000000000000..7d94efcef2d9 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h @@ -0,0 +1,128 @@ +// Copyright (c) 1999, 2007, 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. +// +// Author: Ray Sidney and many others +// +// Defines the VLOG_IS_ON macro that controls the variable-verbosity +// conditional logging. +// +// It's used by VLOG and VLOG_IF in logging.h +// and by RAW_VLOG in raw_logging.h to trigger the logging. +// +// It can also be used directly e.g. like this: +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished e.g. via just VLOG(2) << ...; +// } +// +// The truth value that VLOG_IS_ON(level) returns is determined by +// the three verbosity level flags: +// --v= Gives the default maximal active V-logging level; +// 0 is the default. +// Normally positive values are used for V-logging levels. +// --vmodule= Gives the per-module maximal V-logging levels to override +// the value given by --v. +// E.g. "my_module=2,foo*=3" would change the logging level +// for all code in source files "my_module.*" and "foo*.*" +// ("-inl" suffixes are also disregarded for this matching). +// +// SetVLOGLevel helper function is provided to do limited dynamic control over +// V-logging by overriding the per-module settings given via --vmodule flag. +// +// CAVEAT: --vmodule functionality is not available in non gcc compilers. +// + +#ifndef BASE_VLOG_IS_ON_H_ +#define BASE_VLOG_IS_ON_H_ + +#include "glog/log_severity.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +#if defined(__GNUC__) +// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site. +// (Normally) the first time every VLOG_IS_ON(n) site is hit, +// we determine what variable will dynamically control logging at this site: +// it's either FLAGS_v or an appropriate internal variable +// matching the current source file that represents results of +// parsing of --vmodule flag and/or SetVLOGLevel calls. +#define VLOG_IS_ON(verboselevel) \ + ({ static google::int32* vlocal__ = &google::kLogSiteUninitialized; \ + google::int32 verbose_level__ = (verboselevel); \ + (*vlocal__ >= verbose_level__) && \ + ((vlocal__ != &google::kLogSiteUninitialized) || \ + (google::InitVLOG3__(&vlocal__, &FLAGS_v, \ + __FILE__, verbose_level__))); }) +#else +// GNU extensions not available, so we do not support --vmodule. +// Dynamic value of FLAGS_v always controls the logging level. +#define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel)) +#endif + +// Set VLOG(_IS_ON) level for module_pattern to log_level. +// This lets us dynamically control what is normally set by the --vmodule flag. +// Returns the level that previously applied to module_pattern. +// NOTE: To change the log level for VLOG(_IS_ON) sites +// that have already executed after/during InitGoogleLogging, +// one needs to supply the exact --vmodule pattern that applied to them. +// (If no --vmodule pattern applied to them +// the value of FLAGS_v will continue to control them.) +extern GOOGLE_GLOG_DLL_DECL int SetVLOGLevel(const char* module_pattern, + int log_level); + +// Various declarations needed for VLOG_IS_ON above: ========================= + +// Special value used to indicate that a VLOG_IS_ON site has not been +// initialized. We make this a large value, so the common-case check +// of "*vlocal__ >= verbose_level__" in VLOG_IS_ON definition +// passes in such cases and InitVLOG3__ is then triggered. +extern google::int32 kLogSiteUninitialized; + +// Helper routine which determines the logging info for a particalur VLOG site. +// site_flag is the address of the site-local pointer to the controlling +// verbosity level +// site_default is the default to use for *site_flag +// fname is the current source file name +// verbose_level is the argument to VLOG_IS_ON +// We will return the return value for VLOG_IS_ON +// and if possible set *site_flag appropriately. +extern GOOGLE_GLOG_DLL_DECL bool InitVLOG3__( + google::int32** site_flag, + google::int32* site_default, + const char* fname, + google::int32 verbose_level); + +#endif // BASE_VLOG_IS_ON_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a b/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a new file mode 100644 index 000000000000..c0de874c969c Binary files /dev/null and b/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a differ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a b/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a new file mode 100644 index 000000000000..8d2afaef645d Binary files /dev/null and b/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a differ diff --git a/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc b/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc new file mode 100644 index 000000000000..29ae3280571e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc @@ -0,0 +1,603 @@ +// Copyright (c) 2009, 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. + +// Converts a minidump file to a core file which gdb can read. +// Large parts lifted from the userspace core dumper: +// http://code.google.com/p/google-coredumper/ +// +// Usage: minidump-2-core 1234.dmp > core + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/common/minidump_cpu_x86.h" +#include "common/linux/linux_syscall_support.h" +#include "common/linux/minidump_format_linux.h" + +#if __WORDSIZE == 64 + #define ELF_CLASS ELFCLASS64 + #define Ehdr Elf64_Ehdr + #define Phdr Elf64_Phdr + #define Shdr Elf64_Shdr + #define Nhdr Elf64_Nhdr + #define auxv_t Elf64_auxv_t +#else + #define ELF_CLASS ELFCLASS32 + #define Ehdr Elf32_Ehdr + #define Phdr Elf32_Phdr + #define Shdr Elf32_Shdr + #define Nhdr Elf32_Nhdr + #define auxv_t Elf32_auxv_t +#endif + + +#if defined(__x86_64__) + #define ELF_ARCH EM_X86_64 +#elif defined(__i386__) + #define ELF_ARCH EM_386 +#elif defined(__ARM_ARCH_3__) + #define ELF_ARCH EM_ARM +#elif defined(__mips__) + #define ELF_ARCH EM_MIPS +#endif + +static int usage(const char* argv0) { + fprintf(stderr, "Usage: %s \n", argv0); + return 1; +} + +// Write all of the given buffer, handling short writes and EINTR. Return true +// iff successful. +static bool +writea(int fd, const void* idata, size_t length) { + const uint8_t* data = (const uint8_t*) idata; + + size_t done = 0; + while (done < length) { + ssize_t r; + do { + r = write(fd, data + done, length - done); + } while (r == -1 && errno == EINTR); + + if (r < 1) + return false; + done += r; + } + + return true; +} + +// A range of a mmaped file. +class MMappedRange { + public: + MMappedRange(const void* data, size_t length) + : data_(reinterpret_cast(data)), + length_(length) { + } + + // Get an object of |length| bytes at |offset| and return a pointer to it + // unless it's out of bounds. + const void* GetObject(size_t offset, size_t length) { + if (offset + length < offset) + return NULL; + if (offset + length > length_) + return NULL; + return data_ + offset; + } + + // Get element |index| of an array of objects of length |length| starting at + // |offset| bytes. Return NULL if out of bounds. + const void* GetArrayElement(size_t offset, size_t length, unsigned index) { + const size_t element_offset = offset + index * length; + return GetObject(element_offset, length); + } + + // Return a new range which is a subset of this range. + MMappedRange Subrange(const MDLocationDescriptor& location) const { + if (location.rva > length_ || + location.rva + location.data_size < location.rva || + location.rva + location.data_size > length_) { + return MMappedRange(NULL, 0); + } + + return MMappedRange(data_ + location.rva, location.data_size); + } + + const uint8_t* data() const { return data_; } + size_t length() const { return length_; } + + private: + const uint8_t* const data_; + const size_t length_; +}; + +/* Dynamically determines the byte sex of the system. Returns non-zero + * for big-endian machines. + */ +static inline int sex() { + int probe = 1; + return !*(char *)&probe; +} + +typedef struct elf_timeval { /* Time value with microsecond resolution */ + long tv_sec; /* Seconds */ + long tv_usec; /* Microseconds */ +} elf_timeval; + +typedef struct elf_siginfo { /* Information about signal (unused) */ + int32_t si_signo; /* Signal number */ + int32_t si_code; /* Extra code */ + int32_t si_errno; /* Errno */ +} elf_siginfo; + +typedef struct prstatus { /* Information about thread; includes CPU reg*/ + elf_siginfo pr_info; /* Info associated with signal */ + uint16_t pr_cursig; /* Current signal */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ + pid_t pr_pid; /* Process ID */ + pid_t pr_ppid; /* Parent's process ID */ + pid_t pr_pgrp; /* Group ID */ + pid_t pr_sid; /* Session ID */ + elf_timeval pr_utime; /* User time */ + elf_timeval pr_stime; /* System time */ + elf_timeval pr_cutime; /* Cumulative user time */ + elf_timeval pr_cstime; /* Cumulative system time */ + user_regs_struct pr_reg; /* CPU registers */ + uint32_t pr_fpvalid; /* True if math co-processor being used */ +} prstatus; + +typedef struct prpsinfo { /* Information about process */ + unsigned char pr_state; /* Numeric process state */ + char pr_sname; /* Char for pr_state */ + unsigned char pr_zomb; /* Zombie */ + signed char pr_nice; /* Nice val */ + unsigned long pr_flag; /* Flags */ +#if defined(__x86_64__) || defined(__mips__) + uint32_t pr_uid; /* User ID */ + uint32_t pr_gid; /* Group ID */ +#else + uint16_t pr_uid; /* User ID */ + uint16_t pr_gid; /* Group ID */ +#endif + pid_t pr_pid; /* Process ID */ + pid_t pr_ppid; /* Parent's process ID */ + pid_t pr_pgrp; /* Group ID */ + pid_t pr_sid; /* Session ID */ + char pr_fname[16]; /* Filename of executable */ + char pr_psargs[80]; /* Initial part of arg list */ +} prpsinfo; + +// We parse the minidump file and keep the parsed information in this structure. +struct CrashedProcess { + CrashedProcess() + : crashing_tid(-1), + auxv(NULL), + auxv_length(0) { + memset(&prps, 0, sizeof(prps)); + prps.pr_sname = 'R'; + } + + struct Mapping { + uint64_t start_address, end_address; + }; + std::vector mappings; + + pid_t crashing_tid; + int fatal_signal; + + struct Thread { + pid_t tid; + user_regs_struct regs; + user_fpregs_struct fpregs; + user_fpxregs_struct fpxregs; + uintptr_t stack_addr; + const uint8_t* stack; + size_t stack_length; + }; + std::vector threads; + + const uint8_t* auxv; + size_t auxv_length; + + prpsinfo prps; +}; + +static uint32_t +U32(const uint8_t* data) { + uint32_t v; + memcpy(&v, data, sizeof(v)); + return v; +} + +static uint16_t +U16(const uint8_t* data) { + uint16_t v; + memcpy(&v, data, sizeof(v)); + return v; +} + +#if defined(__i386__) +static void +ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) { + const MDRawContextX86* rawregs = + (const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86)); + + thread->regs.ebx = rawregs->ebx; + thread->regs.ecx = rawregs->ecx; + thread->regs.edx = rawregs->edx; + thread->regs.esi = rawregs->esi; + thread->regs.edi = rawregs->edi; + thread->regs.ebp = rawregs->ebp; + thread->regs.eax = rawregs->eax; + thread->regs.xds = rawregs->ds; + thread->regs.xes = rawregs->es; + thread->regs.xfs = rawregs->fs; + thread->regs.xgs = rawregs->gs; + thread->regs.orig_eax = rawregs->eax; + thread->regs.eip = rawregs->eip; + thread->regs.xcs = rawregs->cs; + thread->regs.eflags = rawregs->eflags; + thread->regs.esp = rawregs->esp; + thread->regs.xss = rawregs->ss; + + thread->fpregs.cwd = rawregs->float_save.control_word; + thread->fpregs.swd = rawregs->float_save.status_word; + thread->fpregs.twd = rawregs->float_save.tag_word; + thread->fpregs.fip = rawregs->float_save.error_offset; + thread->fpregs.fcs = rawregs->float_save.error_selector; + thread->fpregs.foo = rawregs->float_save.data_offset; + thread->fpregs.fos = rawregs->float_save.data_selector; + memcpy(thread->fpregs.st_space, rawregs->float_save.register_area, + 10 * 8); + + thread->fpxregs.cwd = rawregs->float_save.control_word; + thread->fpxregs.swd = rawregs->float_save.status_word; + thread->fpxregs.twd = rawregs->float_save.tag_word; + thread->fpxregs.fop = U16(rawregs->extended_registers + 6); + thread->fpxregs.fip = U16(rawregs->extended_registers + 8); + thread->fpxregs.fcs = U16(rawregs->extended_registers + 12); + thread->fpxregs.foo = U16(rawregs->extended_registers + 16); + thread->fpxregs.fos = U16(rawregs->extended_registers + 20); + thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24); + memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128); + memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128); +} +#else +#error "This code has not been ported to your platform yet" +#endif + +static void +ParseThreadList(CrashedProcess* crashinfo, MMappedRange range, + const MMappedRange& full_file) { + const uint32_t num_threads = + *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); + for (unsigned i = 0; i < num_threads; ++i) { + CrashedProcess::Thread thread; + memset(&thread, 0, sizeof(thread)); + const MDRawThread* rawthread = + (MDRawThread*) range.GetArrayElement(sizeof(uint32_t), + sizeof(MDRawThread), i); + thread.tid = rawthread->thread_id; + thread.stack_addr = rawthread->stack.start_of_memory_range; + MMappedRange stack_range = full_file.Subrange(rawthread->stack.memory); + thread.stack = stack_range.data(); + thread.stack_length = rawthread->stack.memory.data_size; + + ParseThreadRegisters(&thread, + full_file.Subrange(rawthread->thread_context)); + + crashinfo->threads.push_back(thread); + } +} + +static void +ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) { + crashinfo->auxv = range.data(); + crashinfo->auxv_length = range.length(); +} + +static void +ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) { + const char* cmdline = (const char*) range.data(); + for (size_t i = 0; i < range.length(); ++i) { + if (cmdline[i] == 0) { + static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1; + static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1; + memset(crashinfo->prps.pr_fname, 0, fname_len + 1); + memset(crashinfo->prps.pr_psargs, 0, args_len + 1); + const char* binary_name = strrchr(cmdline, '/'); + if (binary_name) { + binary_name++; + const unsigned len = strlen(binary_name); + memcpy(crashinfo->prps.pr_fname, binary_name, + len > fname_len ? fname_len : len); + } else { + memcpy(crashinfo->prps.pr_fname, cmdline, + i > fname_len ? fname_len : i); + } + + const unsigned len = range.length() > args_len ? + args_len : range.length(); + memcpy(crashinfo->prps.pr_psargs, cmdline, len); + for (unsigned i = 0; i < len; ++i) { + if (crashinfo->prps.pr_psargs[i] == 0) + crashinfo->prps.pr_psargs[i] = ' '; + } + } + } +} + +static void +ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) { + const MDRawExceptionStream* exp = + (MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream)); + crashinfo->crashing_tid = exp->thread_id; + crashinfo->fatal_signal = (int) exp->exception_record.exception_code; +} + +static bool +WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) { + struct prstatus pr; + memset(&pr, 0, sizeof(pr)); + + pr.pr_info.si_signo = fatal_signal; + pr.pr_cursig = fatal_signal; + pr.pr_pid = thread.tid; + memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); + + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + nhdr.n_namesz = 5; + nhdr.n_descsz = sizeof(struct prstatus); + nhdr.n_type = NT_PRSTATUS; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &pr, sizeof(struct prstatus))) { + return false; + } + + nhdr.n_descsz = sizeof(user_fpregs_struct); + nhdr.n_type = NT_FPREGSET; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) { + return false; + } + + nhdr.n_descsz = sizeof(user_fpxregs_struct); + nhdr.n_type = NT_PRXFPREG; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "LINUX\0\0\0", 8) || + !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) { + return false; + } + + return true; +} + +static void +ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range) { + const uint32_t num_mappings = + *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); + for (unsigned i = 0; i < num_mappings; ++i) { + CrashedProcess::Mapping mapping; + const MDRawModule* rawmodule = + (MDRawModule*) range.GetArrayElement(sizeof(uint32_t), + MD_MODULE_SIZE, i); + mapping.start_address = rawmodule->base_of_image; + mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image; + + crashinfo->mappings.push_back(mapping); + } +} + +int +main(int argc, char** argv) { + if (argc != 2) + return usage(argv[0]); + + const int fd = open(argv[1], O_RDONLY); + if (fd < 0) + return usage(argv[0]); + + struct stat st; + fstat(fd, &st); + + const void* bytes = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + if (bytes == MAP_FAILED) { + perror("Failed to mmap dump file"); + return 1; + } + + MMappedRange dump(bytes, st.st_size); + + const MDRawHeader* header = + (const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader)); + + CrashedProcess crashinfo; + + for (unsigned i = 0; i < header->stream_count; ++i) { + const MDRawDirectory* dirent = + (const MDRawDirectory*) dump.GetArrayElement( + header->stream_directory_rva, sizeof(MDRawDirectory), i); + switch (dirent->stream_type) { + case MD_THREAD_LIST_STREAM: + ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump); + break; + case MD_LINUX_AUXV: + ParseAuxVector(&crashinfo, dump.Subrange(dirent->location)); + break; + case MD_LINUX_CMD_LINE: + ParseCmdLine(&crashinfo, dump.Subrange(dirent->location)); + break; + case MD_EXCEPTION_STREAM: + ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location)); + break; + case MD_MODULE_LIST_STREAM: + ParseModuleStream(&crashinfo, dump.Subrange(dirent->location)); + default: + fprintf(stderr, "Skipping %x\n", dirent->stream_type); + } + } + + // Write the ELF header. The file will look like: + // ELF header + // Phdr for the PT_NOTE + // Phdr for each of the thread stacks + // PT_NOTE + // each of the thread stacks + Ehdr ehdr; + memset(&ehdr, 0, sizeof(Ehdr)); + ehdr.e_ident[0] = ELFMAG0; + ehdr.e_ident[1] = ELFMAG1; + ehdr.e_ident[2] = ELFMAG2; + ehdr.e_ident[3] = ELFMAG3; + ehdr.e_ident[4] = ELF_CLASS; + ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB; + ehdr.e_ident[6] = EV_CURRENT; + ehdr.e_type = ET_CORE; + ehdr.e_machine = ELF_ARCH; + ehdr.e_version = EV_CURRENT; + ehdr.e_phoff = sizeof(Ehdr); + ehdr.e_ehsize = sizeof(Ehdr); + ehdr.e_phentsize= sizeof(Phdr); + ehdr.e_phnum = 1 + crashinfo.threads.size() + crashinfo.mappings.size(); + ehdr.e_shentsize= sizeof(Shdr); + if (!writea(1, &ehdr, sizeof(Ehdr))) + return 1; + + size_t offset = sizeof(Ehdr) + + (1 + crashinfo.threads.size() + + crashinfo.mappings.size()) * sizeof(Phdr); + size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) + + // sizeof(Nhdr) + 8 + sizeof(user) + + sizeof(Nhdr) + 8 + crashinfo.auxv_length + + crashinfo.threads.size() * ( + (sizeof(Nhdr) + 8 + sizeof(prstatus)) + + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) + + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)); + + Phdr phdr; + memset(&phdr, 0, sizeof(Phdr)); + phdr.p_type = PT_NOTE; + phdr.p_offset = offset; + phdr.p_filesz = filesz; + if (!writea(1, &phdr, sizeof(phdr))) + return 1; + + phdr.p_type = PT_LOAD; + phdr.p_align = getpagesize(); + size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align); + if (note_align == phdr.p_align) + note_align = 0; + offset += note_align; + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + const CrashedProcess::Thread& thread = crashinfo.threads[i]; + offset += filesz; + filesz = thread.stack_length; + phdr.p_offset = offset; + phdr.p_vaddr = thread.stack_addr; + phdr.p_filesz = phdr.p_memsz = filesz; + phdr.p_flags = PF_R | PF_W; + if (!writea(1, &phdr, sizeof(phdr))) + return 1; + } + + for (unsigned i = 0; i < crashinfo.mappings.size(); ++i) { + const CrashedProcess::Mapping& mapping = crashinfo.mappings[i]; + phdr.p_offset = 0; + phdr.p_vaddr = mapping.start_address; + phdr.p_filesz = 0; + phdr.p_flags = PF_R; + phdr.p_memsz = mapping.end_address - mapping.start_address; + if (!writea(1, &phdr, sizeof(phdr))) + return 1; + } + + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + nhdr.n_namesz = 5; + nhdr.n_descsz = sizeof(prpsinfo); + nhdr.n_type = NT_PRPSINFO; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &crashinfo.prps, sizeof(prpsinfo))) { + return 1; + } + + nhdr.n_descsz = crashinfo.auxv_length; + nhdr.n_type = NT_AUXV; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &crashinfo.auxv, crashinfo.auxv_length)) { + return 1; + } + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + if (crashinfo.threads[i].tid == crashinfo.crashing_tid) { + WriteThread(crashinfo.threads[i], crashinfo.fatal_signal); + break; + } + } + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + if (crashinfo.threads[i].tid != crashinfo.crashing_tid) + WriteThread(crashinfo.threads[i], 0); + } + + if (note_align) { + char scratch[note_align]; + memset(scratch, 0, sizeof(scratch)); + if (!writea(1, scratch, sizeof(scratch))) + return 1; + } + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + const CrashedProcess::Thread& thread = crashinfo.threads[i]; + if (!writea(1, thread.stack, thread.stack_length)) + return 1; + } + + munmap(const_cast(bytes), st.st_size); + + return 0; +} diff --git a/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm b/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm index f90ee42ff689..22d2a18e7c0c 100644 --- a/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm +++ b/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm @@ -59,6 +59,7 @@ using google_breakpad::BasicSourceLineResolver; using google_breakpad::CallStack; using google_breakpad::CodeModule; using google_breakpad::CodeModules; +using google_breakpad::Minidump; using google_breakpad::MinidumpProcessor; using google_breakpad::OnDemandSymbolSupplier; using google_breakpad::PathnameStripper; @@ -73,6 +74,7 @@ typedef struct { NSString *minidumpPath; NSString *searchDir; NSString *symbolSearchDir; + BOOL printThreadMemory; } Options; //============================================================================= @@ -220,10 +222,8 @@ static void PrintModules(const CodeModules *modules) { } } -//============================================================================= -static void Start(Options *options) { - string minidump_file([options->minidumpPath fileSystemRepresentation]); - +static void ProcessSingleReport(Options *options, NSString *file_path) { + string minidump_file([file_path fileSystemRepresentation]); BasicSourceLineResolver resolver; string search_dir = options->searchDir ? [options->searchDir fileSystemRepresentation] : ""; @@ -234,8 +234,14 @@ static void Start(Options *options) { scoped_ptr minidump_processor(new MinidumpProcessor(symbol_supplier.get(), &resolver)); ProcessState process_state; - if (minidump_processor->Process(minidump_file, &process_state) != - MinidumpProcessor::PROCESS_OK) { + scoped_ptr dump(new google_breakpad::Minidump(minidump_file)); + + if (!dump->Read()) { + fprintf(stderr, "Minidump %s could not be read\n", dump->path().c_str()); + return; + } + if (minidump_processor->Process(dump.get(), &process_state) != + google_breakpad::PROCESS_OK) { fprintf(stderr, "MinidumpProcessor::Process failed\n"); return; } @@ -274,12 +280,20 @@ static void Start(Options *options) { // Print all of the threads in the dump. int thread_count = process_state.threads()->size(); + const std::vector + *thread_memory_regions = process_state.thread_memory_regions(); + for (int thread_index = 0; thread_index < thread_count; ++thread_index) { if (thread_index != requesting_thread) { // Don't print the crash thread again, it was already printed. printf("\n"); printf("Thread %d\n", thread_index); PrintStack(process_state.threads()->at(thread_index), cpu); + google_breakpad::MinidumpMemoryRegion *thread_stack_bytes = + thread_memory_regions->at(thread_index); + if (options->printThreadMemory) { + thread_stack_bytes->Print(); + } } } @@ -288,11 +302,36 @@ static void Start(Options *options) { printf("\nThread %d:", requesting_thread); PrintRegisters(process_state.threads()->at(requesting_thread), cpu); } - + // Print information about modules PrintModules(process_state.modules()); } +//============================================================================= +static void Start(Options *options) { + NSFileManager *manager = [NSFileManager defaultManager]; + NSString *minidump_path = options->minidumpPath; + BOOL is_dir = NO; + BOOL file_exists = [manager fileExistsAtPath:minidump_path + isDirectory:&is_dir]; + if (file_exists && is_dir) { + NSDirectoryEnumerator *enumerator = + [manager enumeratorAtPath:minidump_path]; + NSString *current_file = nil; + while ((current_file = [enumerator nextObject])) { + if ([[current_file pathExtension] isEqualTo:@"dmp"]) { + printf("Attempting to process report: %s\n", + [current_file cStringUsingEncoding:NSASCIIStringEncoding]); + NSString *full_path = + [minidump_path stringByAppendingPathComponent:current_file]; + ProcessSingleReport(options, full_path); + } + } + } else if (file_exists) { + ProcessSingleReport(options, minidump_path); + } +} + //============================================================================= static void Usage(int argc, const char *argv[]) { fprintf(stderr, "Convert a minidump to a crash report. Breakpad symbol " @@ -303,9 +342,11 @@ static void Usage(int argc, const char *argv[]) { "If modules cannot be found at the paths stored in the " "minidump file, they will be searched for at " "/.\n"); - fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] minidump-file\n", argv[0]); + fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] " + "minidump-file\n", argv[0]); fprintf(stderr, "\t-s: Specify a search directory to use for missing modules\n" - "\t-S: Specify a search directory to use for symbol files\n" + "\t-S: Specify a search directory to use for symbol files\n" + "\t-t: Print thread stack memory in hex\n" "\t-h: Usage\n" "\t-?: Usage\n"); } @@ -315,7 +356,7 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { extern int optind; char ch; - while ((ch = getopt(argc, (char * const *)argv, "S:s:h?")) != -1) { + while ((ch = getopt(argc, (char * const *)argv, "S:s:ht?")) != -1) { switch (ch) { case 's': options->searchDir = [[NSFileManager defaultManager] @@ -329,6 +370,9 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { length:strlen(optarg)]; break; + case 't': + options->printThreadMemory = YES; + break; case 'h': case '?': Usage(argc, argv);