bug 514188 - sync to breakpad revision 437 to pick up Linux client rewrite. r=bsmedberg

This commit is contained in:
Ted Mielczarek 2009-12-14 06:55:28 -05:00
Родитель 1bc4764e56
Коммит aba2a6854a
100 изменённых файлов: 12920 добавлений и 3054 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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 <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
// 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 <cassert>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <linux/limits.h>
// 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 <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
#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*> *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*>* 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<ExceptionHandler *>;
handler_stack_->push_back(this);
if (handler_stack_ == NULL)
handler_stack_ = new std::vector<ExceptionHandler *>;
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<ExceptionHandler *>::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<SignalHandler>(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<struct sigaction*>(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<ThreadArgument*>(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

Просмотреть файл

@ -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 <pthread.h>
#include <map>
#include <string>
#include <signal.h>
#include <vector>
#include <string>
#include "client/linux/handler/minidump_generator.h"
// Context information when exception occured.
struct sigcontex;
#include <signal.h>
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<ExceptionHandler *> *handler_stack_;
static std::vector<ExceptionHandler*> *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<std::pair<int, void *> > old_handlers_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_

Просмотреть файл

@ -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 <pthread.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#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<int *>(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<int>(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;
}

Просмотреть файл

@ -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 <string>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/uio.h>
#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<int*>(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<char*>(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<void*>(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<struct ucred*>(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<int*>(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<struct ucred*>(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);
}

Просмотреть файл

@ -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 <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <functional>
#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<AddressValidatingContext *>(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<void*>(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<ThreadCallback> *thread_callback =
reinterpret_cast<CallbackParam<ThreadCallback> *>(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<PidCallback> 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<PidCallback> callback_param(ResumeThread, NULL);
IterateProcSelfTask(pid_, &callback_param);
}
int LinuxThread::GetThreadCount() const {
return IterateProcSelfTask(pid_, NULL);
}
int LinuxThread::ListThreads(
CallbackParam<ThreadCallback> *thread_callback_param) const {
CallbackParam<PidCallback> 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<void*>(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<void **>(current_ebp);
void **previous_sp = sp;
while (sp && IsAddressMapped((uintptr_t)sp)) {
previous_sp = sp;
sp = reinterpret_cast<void **>(GetNextFrame(sp));
}
return (uintptr_t)previous_sp;
}
int LinuxThread::GetModuleCount() const {
return ListModules(NULL);
}
int LinuxThread::ListModules(
CallbackParam<ModuleCallback> *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<PidCallback> *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<ModuleCallback> 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<uintptr_t>(GetNextFrame(
reinterpret_cast<void**>(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<struct sigcontext*>(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

Просмотреть файл

@ -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 <stdint.h>
#include <sys/user.h>
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<class CallbackFunc>
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<ThreadCallback> *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<ModuleCallback> *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<PidCallback> *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__

Просмотреть файл

@ -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 <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#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<LinuxThread *>(context);
memset(&regs, 0, sizeof(regs));
if (threads->GetRegisters(thread_info.pid, &regs)) {
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<ModuleCallback> module_callback(ProcessOneModule, &threads);
threads.ListModules(&module_callback);
CallbackParam<ThreadCallback> 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;
}

Просмотреть файл

@ -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 <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <string.h>
#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<FindCrashThreadContext *>(context);
const LinuxThread *thread_lister = crashing_context->thread_lister;
struct user_regs_struct regs;
if (thread_lister->GetRegisters(thread_info.pid, &regs)) {
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<ThreadCallback> 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<void*>(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<MDRawContextX86> 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(&regs, 0, sizeof(regs));
if (!thread_lister->GetRegisters(thread_info.pid, &regs)) {
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<MDRawContextX86> context(minidump_writer);
if (!context.Allocate())
return false;
thread->thread_context = context.location();
memset(context.get(), 0, sizeof(MDRawContextX86));
return WriteContext(context.get(), &regs, &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<MDRawThreadList> *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<ThreadInfoCallbackCtx *>(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<MDRawThreadList> 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<ThreadCallback> 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<MDCVInfoPDB70> 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 = "<Unknown>";
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<char *>(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<MDRawModuleList> *list;
int module_index;
};
bool ModuleInfoCallback(const ModuleInfo &module_info,
void *context) {
ModuleInfoCallbackCtx *callback_context =
static_cast<ModuleInfoCallbackCtx *>(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<MDRawModuleList> 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<ModuleCallback> callback(ModuleInfoCallback, &context);
return writer_args->thread_lister->ListModules(&callback) == module_count;
}
bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer,
const WriterArgument *writer_args,
MDRawDirectory *dir) {
TypedMDRVA<MDRawSystemInfo> 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<MDRawExceptionStream> 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<MDRawContextX86> 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<MDRawMiscInfo> 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<MDRawBreakpadInfo> 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<WriterArgument *>(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<MDRawHeader> header(minidump_writer);
TypedMDRVA<MDRawDirectory> 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

Просмотреть файл

@ -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 <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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<kernel_dirent*>(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<kernel_dirent*>(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_

Просмотреть файл

@ -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 <set>
#include <string>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#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<std::string> 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);
}

Просмотреть файл

@ -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 <stdint.h>
#include <assert.h>
#include <string.h>
#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_

Просмотреть файл

@ -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 <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#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);
}

Просмотреть файл

@ -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 <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#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<void*>(one_aux_entry.a_un.a_val);
}
}
close(fd);
return NULL;
}
bool
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* 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<void*>(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<pid_t>* 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<void*> (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<uint8_t*>(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<ptrdiff_t>(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<uintptr_t>(mappings_[i]->start_addr);
if (addr >= start && addr - start < mappings_[i]->size)
return mappings_[i];
}
return NULL;
}
} // namespace google_breakpad

Просмотреть файл

@ -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 <elf.h>
#include <linux/limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/user.h>
#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<pid_t> &threads() { return threads_; }
const wasteful_vector<MappingInfo*> &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<MappingInfo*>* result) const;
bool EnumerateThreads(wasteful_vector<pid_t>* result) const;
const pid_t pid_;
mutable PageAllocator allocator_;
bool threads_suspened_;
wasteful_vector<pid_t> threads_; // the ids of all the threads
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_

Просмотреть файл

@ -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 <unistd.h>
#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<void*>(getpid)));
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(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<MappingInfo*> 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<void*>(mapping->start_addr));
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
}

Просмотреть файл

@ -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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <sys/utsname.h>
#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<MDRawHeader> header(&minidump_writer_);
TypedMDRVA<MDRawDirectory> 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<uint32_t> 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<RawContextCPU> 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<RawContextCPU> 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<uint32_t> 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<MDRawExceptionStream> 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<MDRawSystemInfo> 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<const ExceptionHandler::CrashContext*>(blob);
MinidumpWriter writer(filename, crashing_process, context);
if (!writer.Init())
return false;
return writer.Dump();
}
} // namespace google_breakpad

Просмотреть файл

@ -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 <stdint.h>
#include "google_breakpad/common/breakpad_types.h"
#include "processor/scoped_ptr.h"
struct sigcontext;
#include <unistd.h>
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<char> 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_

Просмотреть файл

@ -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 <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#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]);
}

Просмотреть файл

@ -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 <string>
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();
}

Просмотреть файл

@ -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 = "<group>"; };
F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = "<group>"; };
F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = "<group>"; };
@ -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 = "<group>";
};
F95BB87D101F949F00AA053B /* Products */ = {
isa = PBXGroup;
children = (
F95BB885101F949F00AA053B /* crash_report */,
);
name = Products;
sourceTree = "<group>";
};
F95BB88A101F94AC00AA053B /* Products */ = {
isa = PBXGroup;
children = (
F95BB892101F94AC00AA053B /* dump_syms */,
);
name = Products;
sourceTree = "<group>";
};
F95BB895101F94C000AA053B /* Products */ = {
isa = PBXGroup;
children = (
F95BB89F101F94C000AA053B /* symupload */,
F95BB8A1101F94C000AA053B /* minidump_upload */,
);
name = Products;
sourceTree = "<group>";
};
F95BB8A3101F94C300AA053B /* Tools */ = {
isa = PBXGroup;
children = (
F95BB894101F94C000AA053B /* symupload.xcodeproj */,
F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */,
F95BB87C101F949F00AA053B /* crash_report.xcodeproj */,
);
name = Tools;
sourceTree = "<group>";
};
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 */;

Просмотреть файл

@ -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.

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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/<kDefaultLibrarySubdirectory>/<Product Name>
if (0 == strlen(minidumpDirectory)) {
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
NSArray *libraryDirectories =
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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:

Просмотреть файл

@ -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

Просмотреть файл

@ -4,6 +4,14 @@
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>CLASS</key>
<string>LengthLimitingTextField</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSTextField</string>
</dict>
<dict>
<key>ACTIONS</key>
<dict>
@ -26,10 +34,14 @@
<string>NSButton</string>
<key>commentMessage_</key>
<string>NSTextField</string>
<key>commentsEntryField_</key>
<string>LengthLimitingTextField</string>
<key>countdownLabel_</key>
<string>NSTextField</string>
<key>dialogTitle_</key>
<string>NSTextField</string>
<key>emailEntryField_</key>
<string>NSView</string>
<string>LengthLimitingTextField</string>
<key>emailLabel_</key>
<string>NSTextField</string>
<key>emailMessage_</key>

Просмотреть файл

@ -3,17 +3,17 @@
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>672</string>
<string>676</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../Breakpad.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>2</integer>
<integer>132</integer>
</array>
<key>IBSystem Version</key>
<string>9G55</string>
<string>9J61</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>

Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib сгенерированный

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -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

Просмотреть файл

@ -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
//=============================================================================

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Просмотреть файл

@ -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

Просмотреть файл

@ -37,6 +37,8 @@
#include <string.h>
#include <unistd.h>
#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<off_t>(position))
if (write(file_, src, size) == size)
#if __linux__
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
if (sys_write(file_, src, size) == size) {
#else
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
if (write(file_, src, size) == size) {
#endif
return true;
}
}
return false;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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,

Просмотреть файл

@ -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<ExceptionHandler*>::iterator iterator =
handler_stack_->begin();
iterator != handler_stack_->end();
++iterator) {
vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin();
while (iterator != handler_stack_->end()) {
if (*iterator == this) {
handler_stack_->erase(iterator);
iterator = handler_stack_->erase(iterator);
} else {
++iterator;
}
}
}

Просмотреть файл

@ -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 <windows.h>
#include <dbghelp.h>
#include <strsafe.h>
#include <objbase.h>
#include <shellapi.h>
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);
}
}

Просмотреть файл

@ -0,0 +1,266 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="exception_handler_test"
ProjectGUID="{89094A11-CF25-4037-AF43-EACFA751405E}"
RootNamespace="exception_handler_test"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine=""
Outputs=""
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\..\..\testing;..\..\..\..\testing\include;..\..\..\..\testing\gtest;..\..\..\..\testing\gtest\include;..\..\..\..\"
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Running tests"
CommandLine="$(OutDir)\$(ProjectName).exe"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\..\..\testing;..\..\..\..\testing\include;..\..\..\..\testing\gtest;..\..\..\..\testing\gtest\include;..\..\..\..\"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Running tests"
CommandLine="$(OutDir)\$(ProjectName).exe"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\crash_generation\client_info.cc"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_client.cc"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_server.cc"
>
</File>
<File
RelativePath="..\exception_handler.cc"
>
</File>
<File
RelativePath=".\exception_handler_test.cc"
>
</File>
<File
RelativePath="..\..\..\..\testing\gtest\src\gtest-all.cc"
>
</File>
<File
RelativePath="..\..\..\..\testing\gtest\src\gtest_main.cc"
>
</File>
<File
RelativePath="..\..\..\..\common\windows\guid_string.cc"
>
</File>
<File
RelativePath="..\..\crash_generation\minidump_generator.cc"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\crash_generation\client_info.h"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_client.h"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_server.h"
>
</File>
<File
RelativePath="..\exception_handler.h"
>
</File>
<File
RelativePath="..\..\..\..\common\windows\guid_string.h"
>
</File>
<File
RelativePath="..\..\crash_generation\minidump_generator.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<File
RelativePath=".\ReadMe.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

Просмотреть файл

@ -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);

Просмотреть файл

@ -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 <a.out.h>
#include <cstdarg>
#include <cstdlib>
#include <cstdio>
#include <assert.h>
#include <cxxabi.h>
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <link.h>
#include <string.h>
#include <sys/mman.h>
#include <stab.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <algorithm>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <list>
#include <map>
#include <string>
#include <vector>
#include <string.h>
#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<struct LineInfo> 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<struct FuncInfo> 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<struct SourceFileInfo> 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<ElfW(Word)>(obj_base);
ElfW(Addr) base = reinterpret_cast<ElfW(Addr)>(obj_base);
ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(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<char*>(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<char *>(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<Module::Function *> functions_;
// Comapre the address.
// The argument should have a memeber named "addr"
template<class T1, class T2>
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<Module::Address> 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<class Container>
static std::vector<typename Container::value_type *> SortByAddress(
Container *container) {
typedef typename Container::iterator It;
typedef typename Container::value_type T;
std::vector<T *> 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<T, T>));
// 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<struct FuncInfo *> *sorted_functions,
std::vector<struct SourceFileInfo *> *sorted_files,
const struct FuncInfo &func_info) {
std::vector<struct FuncInfo *>::iterator next_func_iter =
std::find_if(sorted_functions->begin(),
sorted_functions->end(),
std::bind1st(
std::ptr_fun(
CompareAddress<struct FuncInfo,
struct FuncInfo>
),
&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<struct SourceFileInfo *>::iterator next_file_iter =
std::find_if(sorted_files->begin(),
sorted_files->end(),
std::bind1st(
std::ptr_fun(
CompareAddress<struct FuncInfo,
struct SourceFileInfo>
),
&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<char *>(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<struct SourceFileInfo *> 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<struct FuncInfo *> 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<class T>
// 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<Module::Address>(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<Module::Address>(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<Module::Address>(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<Module::Address>(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<Module::Function *>::iterator func_it = functions_.begin();
func_it != functions_.end();
func_it++) {
Module::Function *f = *func_it;
// Compute the function f's size.
vector<Module::Address>::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<Module::Line>::iterator last_line = f->lines_.end() - 1;
for (vector<Module::Line>::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<struct nlist *>(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 <address> <length> <param_stack_size> <function>
struct SourceFileInfo source_file_info;
source_file_info.name_index = cur_list->n_un.n_strx;
source_file_info.name = reinterpret_cast<char *>(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<uint8_t *>(stab_section->sh_offset);
uint8_t *stabstr = reinterpret_cast<uint8_t *>(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<ElfW(Phdr) *>(elf_header->e_phoff),
elf_header->e_phnum);
module->SetLoadAddress(loading_addr);
const ElfW(Shdr) *sections =
reinterpret_cast<ElfW(Shdr) *>(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<ElfW(Ehdr) *>(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

Просмотреть файл

@ -32,104 +32,74 @@
// See file_id.h for documentation
//
#include <cassert>
#include <cstdio>
#include "common/linux/file_id.h"
#include <arpa/inet.h>
#include <elf.h>
#include <fcntl.h>
#include <link.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include "common/linux/file_id.h"
#include "common/md5.h"
#include <cassert>
#include <cstdio>
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<const unsigned char *>(elf_mapped_base);
const ElfW(Ehdr) *elf_header =
reinterpret_cast<const ElfW(Ehdr) *>(elf_base);
if (memcmp(elf_header, ELFMAG, SELFMAG) != 0)
return false;
*text_start = NULL;
*text_size = 0;
const ElfW(Shdr) *sections =
reinterpret_cast<const ElfW(Shdr) *>(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 = &sections[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<const unsigned char*>(text_section),
text_size);
MD5Final(identifier, &md5);
success = true;
memset(identifier, 0, kMDGUIDSize);
uint8_t* ptr = reinterpret_cast<uint8_t*>(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<uint32_t*>(identifier_swapped);
*data1 = htonl(*data1);
uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
*data2 = htons(*data2);
uint16_t* data3 = reinterpret_cast<uint16_t*>(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++] = '-';

Просмотреть файл

@ -35,25 +35,30 @@
#include <limits.h>
#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__

Просмотреть файл

@ -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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
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);
}
}

Просмотреть файл

@ -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 <string>
#include <map>
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<std::string, std::string> parameters_;
};
}

Просмотреть файл

@ -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<std::string, std::string>& 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());
}
}

Просмотреть файл

@ -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 <curl/curl.h>
#include <curl/easy.h>
#include <curl/types.h>
#include <dlfcn.h>
#include <string>
#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<std::string *>(userp);
size_t real_size = size * nmemb;
response->append(reinterpret_cast<char *>(ptr), real_size);
return real_size;
}
bool LibcurlWrapper::SendRequest(const std::string& url,
const std::map<std::string, std::string>& parameters,
std::string* server_response) {
(*easy_setopt_)(curl_, CURLOPT_URL, url.c_str());
std::map<std::string, std::string>::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<void *>(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;
}
}

Просмотреть файл

@ -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 <string>
#include <map>
#include <curl/curl.h>
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<std::string, std::string>& 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 *);
};
}

Просмотреть файл

@ -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 <stdint.h>
#include <limits.h>
#include <sys/types.h>
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_

Просмотреть файл

@ -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, '-');
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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 <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#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<PageHeader*>(a);
header->next = last_;
header->num_pages = num_pages;
last_ = header;
return reinterpret_cast<uint8_t*>(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 T>
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<T*>(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_

Просмотреть файл

@ -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<uint8_t*>(allocator.Alloc(i));
ASSERT_FALSE(p == NULL);
memset(p, 0, i);
}
}
TEST(PageAllocatorTest, LargeObject) {
PageAllocator allocator;
uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(10000));
ASSERT_FALSE(p == NULL);
for (unsigned i = 1; i < 10; ++i) {
uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i));
ASSERT_FALSE(p == NULL);
memset(p, 0, i);
}
}
namespace {
typedef testing::Test WastefulVectorTest;
}
TEST(WastefulVectorTest, Setup) {
PageAllocator allocator_;
wasteful_vector<int> v(&allocator_);
ASSERT_EQ(v.size(), 0u);
}
TEST(WastefulVectorTest, Simple) {
PageAllocator allocator_;
wasteful_vector<int> 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);
}

Просмотреть файл

@ -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 <cerrno>
#include <cstring>
#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<Function *>::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<Function *>::iterator begin,
vector<Function *>::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<Function *>::const_iterator func_it = functions_.begin();
func_it != functions_.end(); func_it++) {
Function *func = *func_it;
for (vector<Line>::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<Function *>::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<Line>::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

Просмотреть файл

@ -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 <map>
#include <string>
#include <vector>
#include <cstdio>
#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<Line> 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<Function *>::iterator begin,
vector<Function *>::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<const string *, File *, CompareStringPtrs> 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<Function *> functions_; // This module's functions.
};
} // namespace google_breakpad
#endif // COMMON_LINUX_MODULE_H__

Просмотреть файл

@ -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 <a.out.h>
#include <stab.h>
#include <cstring>
#include <cassert>
#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<const struct nlist *>(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<const char *>(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

Просмотреть файл

@ -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 <a.out.h> and <stab.h>
// 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 <stdint.h>
#include <cstddef>
#include <a.out.h>
#include <string>
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<char *, ...>, 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__

Просмотреть файл

@ -31,6 +31,8 @@
// Simple string dictionary that does not allocate memory
//
#include <assert.h>
#import "SimpleStringDictionary.h"
namespace google_breakpad {

Просмотреть файл

@ -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<string, dwarf2reader::SectionMap *> ArchSectionMap;
typedef map<string, dwarf2reader::SectionMap *> ArchSectionMap;
@interface DumpSymbols : NSObject {
@protected

Просмотреть файл

@ -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<std::string> {
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_];

Просмотреть файл

@ -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<uint16>(buffer[0]);
const uint16 buffer1 = static_cast<uint16>(buffer[1]);
inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(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<uint32>(buffer[0]);
const uint32 buffer1 = static_cast<uint32>(buffer[1]);
const uint32 buffer2 = static_cast<uint32>(buffer[2]);
const uint32 buffer3 = static_cast<uint32>(buffer[3]);
inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(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<uint64>(buffer[0]);
const uint64 buffer1 = static_cast<uint64>(buffer[1]);
const uint64 buffer2 = static_cast<uint64>(buffer[2]);
const uint64 buffer3 = static_cast<uint64>(buffer[3]);
const uint64 buffer4 = static_cast<uint64>(buffer[4]);
const uint64 buffer5 = static_cast<uint64>(buffer[5]);
const uint64 buffer6 = static_cast<uint64>(buffer[6]);
const uint64 buffer7 = static_cast<uint64>(buffer[7]);
inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(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;

Просмотреть файл

@ -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 <assert.h>
#include "common/mac/dwarf/bytereader-inl.h"
#include "common/mac/dwarf/bytereader.h"
namespace dwarf2reader {

Просмотреть файл

@ -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 <ext/hash_map>
#include <assert.h>
#include <stack>
#include <utility>
@ -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

Просмотреть файл

@ -36,8 +36,8 @@
#ifndef COMMON_MAC_DWARF_DWARF2READER_H__
#define COMMON_MAC_DWARF_DWARF2READER_H__
#include <ext/hash_map>
#include <list>
#include <map>
#include <string>
#include <utility>
#include <vector>
@ -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<string, pair<const char*, uint64> > SectionMap;
typedef map<string, pair<const char*, uint64> > SectionMap;
typedef list<pair<enum DwarfAttribute, enum DwarfForm> > AttributeList;
typedef AttributeList::iterator AttributeIterator;
typedef AttributeList::const_iterator ConstAttributeIterator;

Просмотреть файл

@ -29,26 +29,17 @@
// This is a client for the dwarf2reader to extract function and line
// information from the debug info.
#include <assert.h>
#include <map>
#include <queue>
#include <vector>
#include "common/mac/dwarf/functioninfo.h"
#include "common/mac/dwarf/bytereader.h"
namespace __gnu_cxx
{
template<>
struct hash<std::string>
{
size_t operator()(const std::string& k) const;
};
}
namespace dwarf2reader {
// Given an offset value, its form, and the base offset of the

Просмотреть файл

@ -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 <address> <length> <param_stack_size> <function>
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<IDiaSymbol> global;
if (FAILED(session_->get_globalScope(&global))) {
fprintf(stderr, "get_globalScope failed\n");
return false;
}
CComPtr<IDiaEnumSymbols> compilands;
if (FAILED(global->findChildren(SymTagCompiland, NULL,
nsNone, &compilands))) {
fprintf(stderr, "findChildren failed on the global\n");
return false;
}
CComPtr<IDiaSymbol> compiland;
while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
CComPtr<IDiaEnumSymbols> blocks;
if (FAILED(compiland->findChildren(SymTagBlock, NULL,
nsNone, &blocks))) {
fprintf(stderr, "findChildren failed on a compiland\n");
return false;
}
CComPtr<IDiaSymbol> block;
while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) {
// find this block's lexical parent function
CComPtr<IDiaSymbol> 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;
}

Просмотреть файл

@ -35,6 +35,7 @@
#include <atlcomcli.h>
#include <hash_map>
#include <string>
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<wstring, DWORD>::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<DWORD, DWORD>::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<DWORD, DWORD> file_ids_;
// This maps unique filenames to file IDs.
hash_map<wstring, DWORD> unique_files_;
// Disallow copy ctor and operator=
PDBSourceLineWriter(const PDBSourceLineWriter&);
void operator=(const PDBSourceLineWriter&);

Просмотреть файл

@ -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;

Просмотреть файл

@ -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 <map>
#else // BSLR_NO_HASH_MAP
#include <ext/hash_map>
#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<string, Module*, CompareString> ModuleMap;
#else // BSLR_NO_HASH_MAP
typedef hash_map<string, Module*, HashString> ModuleMap;
#endif // BSLR_NO_HASH_MAP
ModuleMap *modules_;
// Disallow unwanted copy ctor and assignment operator

Просмотреть файл

@ -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();

Просмотреть файл

@ -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_;

Просмотреть файл

@ -36,8 +36,9 @@
#include <string>
#include <vector>
#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<CallStack*>* threads() const { return &threads_; }
const vector<MinidumpMemoryRegion*>* 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<CallStack*> threads_;
vector<MinidumpMemoryRegion*> thread_memory_regions_;
// OS and CPU information.
SystemInfo system_info_;

Просмотреть файл

@ -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 {

Просмотреть файл

@ -42,6 +42,7 @@
#define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
#include <vector>
#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_;

Просмотреть файл

@ -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<int, string> FileMap;
#else // BSLR_NO_HASH_MAP
typedef hash_map<int, string> 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<const char*>()(s.c_str());
}
#endif // BSLR_NO_HASH_MAP
} // namespace google_breakpad

Просмотреть файл

@ -243,6 +243,15 @@ static string* UTF16ToUTF8(const vector<u_int16_t>& 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<MinidumpContext> 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<u_int16_t> expression_utf16(word_length);
memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length);
scoped_ptr<string> 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<u_int16_t> function_utf16(word_length);
memcpy(&function_utf16[0], &assertion_.function[0], byte_length);
scoped_ptr<string> 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<u_int16_t> file_utf16(word_length);
memcpy(&file_utf16[0], &assertion_.file[0], byte_length);
scoped_ptr<string> 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;

Просмотреть файл

@ -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;

Просмотреть файл

@ -28,6 +28,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#include <cstdio>
#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

Просмотреть файл

@ -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<const StackFramePPC*>(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) {

Просмотреть файл

@ -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 <cstdio>
#include "processor/pathname_stripper.h"
#include "processor/logging.h"

Просмотреть файл

@ -38,6 +38,7 @@
#define PROCESSOR_POSTFIX_EVALUATOR_INL_H__
#include <cstdio>
#include <sstream>
#include "processor/postfix_evaluator.h"

Просмотреть файл

@ -48,6 +48,7 @@ void ProcessState::Clear() {
crashed_ = false;
crash_reason_.clear();
crash_address_ = 0;
assertion_.clear();
requesting_thread_ = -1;
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
iterator != threads_.end();

Просмотреть файл

@ -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

Просмотреть файл

@ -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<StackFrameX86*>(
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

Просмотреть файл

@ -70,6 +70,19 @@ class StackwalkerX86 : public Stackwalker {
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &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_;

Просмотреть файл

@ -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)

Просмотреть файл

@ -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 <string>
#include <vector>
// 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 <stdint.h> // the normal place uint16_t is defined
#endif
#if 1
#include <sys/types.h> // the normal place u_int16_t is defined
#endif
#if 1
#include <inttypes.h> // 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<CommandLineFlagInfo>* 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<std::string>& 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] + " <uselessarg1> <uselessarg2>";
// 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_<name>, we also
// preemptively define a junk variable, FLAGS_no<name>. 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<name>. This serves the second purpose of assuring a
// compile error if someone tries to define a flag named no<name>
// 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<typename From> 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<std::string*>(s_##name[0].s)); \
} \
using fLS::FLAGS_##name
#endif // SWIG
#endif // GOOGLE_GFLAGS_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 <TAB> 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<TAB> Show me all flags with names prefixed by 'foo'
// --foo?<TAB> Show me all flags with 'foo' somewhere in the name
// --foo??<TAB> Same as prior case, but also search in module
// definition path for 'foo'
// --foo???<TAB> 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<TAB>
// Or:
// $ ./bin/path/another_binary --gfs_u<TAB>
// (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<TAB>
// 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_

Просмотреть файл

@ -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__

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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 <time.h>
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_

Просмотреть файл

@ -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<string> x;
// LOG(INFO) << "data: " << x;
// vector<int> 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 <deque>
#include <list>
#include <map>
#include <ostream>
#include <set>
#include <utility>
#include <vector>
#ifdef __GNUC__
# include <ext/hash_set>
# include <ext/hash_map>
# include <ext/slist>
#endif
template<class First, class Second>
inline std::ostream& operator<<(std::ostream& out,
const std::pair<First, Second>& p) {
out << '(' << p.first << ", " << p.second << ')';
return out;
}
namespace google {
template<class Iter>
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<class T1, class T2> \
inline std::ostream& operator<<(std::ostream& out, \
const Sequence<T1, T2>& 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<class T1, class T2, class T3> \
inline std::ostream& operator<<(std::ostream& out, \
const Sequence<T1, T2, T3>& 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<class T1, class T2, class T3, class T4> \
inline std::ostream& operator<<(std::ostream& out, \
const Sequence<T1, T2, T3, T4>& 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<class T1, class T2, class T3, class T4, class T5> \
inline std::ostream& operator<<(std::ostream& out, \
const Sequence<T1, T2, T3, T4, T5>& 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_

Просмотреть файл

@ -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=<n> Gives the default maximal active V-logging level;
// 0 is the default.
// Normally positive values are used for V-logging levels.
// --vmodule=<str> 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_

Двоичные данные
toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a поставляемый Normal file

Двоичный файл не отображается.

Двоичные данные
toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a поставляемый Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -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 <vector>
#include <stdio.h>
#include <string.h>
#include <elf.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/user.h>
#include <sys/mman.h>
#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 <minidump file>\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<const uint8_t*>(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<Mapping> 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<Thread> 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<void*>(bytes), st.st_size);
return 0;
}

Просмотреть файл

@ -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<MinidumpProcessor>
minidump_processor(new MinidumpProcessor(symbol_supplier.get(), &resolver));
ProcessState process_state;
if (minidump_processor->Process(minidump_file, &process_state) !=
MinidumpProcessor::PROCESS_OK) {
scoped_ptr<Minidump> 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<google_breakpad::MinidumpMemoryRegion*>
*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 "
"<module-search-dir>/<path-in-minidump-file>.\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);