mirror of
				https://github.com/yuzu-emu/breakpad.git
				synced 2025-11-04 16:04:58 +00:00 
			
		
		
		
	Merge of Breakpad Chrome Linux fork
A=agl, Lei Zhang R=nealsid, agl git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@384 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
		
							parent
							
								
									7c48629d49
								
							
						
					
					
						commit
						b0baafc4da
					
				| 
						 | 
				
			
			@ -1,55 +1,45 @@
 | 
			
		|||
CXX=g++
 | 
			
		||||
CC=gcc
 | 
			
		||||
 | 
			
		||||
CXXFLAGS=-gstabs+ -I../../.. -Wall -D_REENTRANT
 | 
			
		||||
CXXFLAGS=-gstabs+ -I../../../ -I../../../testing/gtest/include -I../../../testing/include -I../../../testing/gtest -D_REENTRANT -m32
 | 
			
		||||
CFLAGS=$(CXXFLAGS)
 | 
			
		||||
LDFLAGS=-lpthread
 | 
			
		||||
 | 
			
		||||
OBJ_DIR=.
 | 
			
		||||
BIN_DIR=.
 | 
			
		||||
 | 
			
		||||
THREAD_SRC=linux_thread.cc
 | 
			
		||||
SHARE_SRC=../../minidump_file_writer.cc\
 | 
			
		||||
	  ../../../common/string_conversion.cc\
 | 
			
		||||
	  ../../../common/linux/file_id.cc\
 | 
			
		||||
	  minidump_generator.cc
 | 
			
		||||
HANDLER_SRC=exception_handler.cc\
 | 
			
		||||
	    ../../../common/linux/guid_creator.cc
 | 
			
		||||
SHARE_C_SRC=../../../common/convert_UTF.c
 | 
			
		||||
TEST_CC_SRC=exception_handler_unittest.cc \
 | 
			
		||||
	    exception_handler.cc \
 | 
			
		||||
	    ../../../testing/gtest/src/gtest-all.cc \
 | 
			
		||||
	    ../../../common/linux/guid_creator.cc \
 | 
			
		||||
	    ../minidump_writer/minidump_writer.cc \
 | 
			
		||||
	    ../../minidump_file_writer.cc \
 | 
			
		||||
	    ../minidump_writer/linux_dumper.cc \
 | 
			
		||||
	    ../../../testing/gtest/src/gtest_main.cc \
 | 
			
		||||
	    ../../../common/string_conversion.cc \
 | 
			
		||||
	    ../minidump_writer/directory_reader_unittest.cc \
 | 
			
		||||
	    ../minidump_writer/line_reader_unittest.cc \
 | 
			
		||||
	    ../minidump_writer/linux_dumper_unittest.cc \
 | 
			
		||||
	    ../minidump_writer/minidump_writer_unittest.cc
 | 
			
		||||
 | 
			
		||||
THREAD_TEST_SRC=linux_thread_test.cc
 | 
			
		||||
MINIDUMP_TEST_SRC=minidump_test.cc
 | 
			
		||||
EXCEPTION_TEST_SRC=exception_handler_test.cc
 | 
			
		||||
TEST_C_SRC = ../../../common/convert_UTF.c
 | 
			
		||||
 | 
			
		||||
THREAD_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(THREAD_SRC))
 | 
			
		||||
SHARE_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(SHARE_SRC))
 | 
			
		||||
HANDLER_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(HANDLER_SRC))
 | 
			
		||||
SHARE_C_OBJ=$(patsubst %.c,$(OBJ_DIR)/%.o,$(SHARE_C_SRC)) md5.o
 | 
			
		||||
THREAD_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(THREAD_TEST_SRC))\
 | 
			
		||||
		$(THREAD_OBJ)
 | 
			
		||||
MINIDUMP_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(MINIDUMP_TEST_SRC))\
 | 
			
		||||
		  $(THREAD_OBJ) $(SHARE_OBJ) $(SHARE_C_OBJ)
 | 
			
		||||
EXCEPTION_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(EXCEPTION_TEST_SRC))\
 | 
			
		||||
		   $(THREAD_OBJ) $(SHARE_OBJ) $(SHARE_C_OBJ) $(HANDLER_OBJ)
 | 
			
		||||
TEST_CC_OBJ=$(patsubst %.cc, $(OBJ_DIR)/%.o,$(TEST_CC_SRC))
 | 
			
		||||
TEST_C_OBJ=$(patsubst %.c, $(OBJ_DIR)/%.o, $(TEST_C_SRC))
 | 
			
		||||
 | 
			
		||||
BIN=$(BIN_DIR)/minidump_test\
 | 
			
		||||
    $(BIN_DIR)/linux_thread_test\
 | 
			
		||||
    $(BIN_DIR)/exception_handler_test
 | 
			
		||||
LINUX_CLIENT_BIN=$(BIN_DIR)/linux_client_test
 | 
			
		||||
 | 
			
		||||
BIN=$(LINUX_CLIENT_BIN)
 | 
			
		||||
 | 
			
		||||
.PHONY:all clean
 | 
			
		||||
 | 
			
		||||
check:$(BIN)
 | 
			
		||||
	$(LINUX_CLIENT_BIN)
 | 
			
		||||
 | 
			
		||||
all:$(BIN)
 | 
			
		||||
 | 
			
		||||
$(BIN_DIR)/linux_thread_test:$(THREAD_TEST_OBJ)
 | 
			
		||||
$(BIN_DIR)/linux_client_test:$(TEST_CC_OBJ) $(TEST_C_OBJ)
 | 
			
		||||
	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
 | 
			
		||||
 | 
			
		||||
$(BIN_DIR)/minidump_test:$(MINIDUMP_TEST_OBJ)
 | 
			
		||||
	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
 | 
			
		||||
 | 
			
		||||
$(BIN_DIR)/exception_handler_test:$(EXCEPTION_TEST_OBJ)
 | 
			
		||||
	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
 | 
			
		||||
 | 
			
		||||
md5.o:../../../common/md5.c
 | 
			
		||||
	$(CC) $(CXXFLAGS) -c $^
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f $(BIN) *.o *.dmp
 | 
			
		||||
	rm -f $(BIN) $(TEST_CC_OBJ) $(TEST_C_OBJ)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,48 +27,87 @@
 | 
			
		|||
// (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 <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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
| 
						 | 
				
			
			@ -79,212 +116,76 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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];
 | 
			
		||||
| 
						 | 
				
			
			@ -302,4 +203,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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										256
									
								
								src/client/linux/handler/exception_handler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								src/client/linux/handler/exception_handler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 = (int) 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 = (int) 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(®s, 0, sizeof(regs));
 | 
			
		||||
  if (threads->GetRegisters(thread_info.pid, ®s)) {
 | 
			
		||||
    printf("  gs                           = 0x%lx\n", regs.xgs);
 | 
			
		||||
    printf("  fs                           = 0x%lx\n", regs.xfs);
 | 
			
		||||
    printf("  es                           = 0x%lx\n", regs.xes);
 | 
			
		||||
    printf("  ds                           = 0x%lx\n", regs.xds);
 | 
			
		||||
    printf("  edi                          = 0x%lx\n", regs.edi);
 | 
			
		||||
    printf("  esi                          = 0x%lx\n", regs.esi);
 | 
			
		||||
    printf("  ebx                          = 0x%lx\n", regs.ebx);
 | 
			
		||||
    printf("  edx                          = 0x%lx\n", regs.edx);
 | 
			
		||||
    printf("  ecx                          = 0x%lx\n", regs.ecx);
 | 
			
		||||
    printf("  eax                          = 0x%lx\n", regs.eax);
 | 
			
		||||
    printf("  ebp                          = 0x%lx\n", regs.ebp);
 | 
			
		||||
    printf("  eip                          = 0x%lx\n", regs.eip);
 | 
			
		||||
    printf("  cs                           = 0x%lx\n", regs.xcs);
 | 
			
		||||
    printf("  eflags                       = 0x%lx\n", regs.eflags);
 | 
			
		||||
    printf("  esp                          = 0x%lx\n", regs.esp);
 | 
			
		||||
    printf("  ss                           = 0x%lx\n", regs.xss);
 | 
			
		||||
  } else {
 | 
			
		||||
    fprintf(stderr, "ERROR: Failed to get general purpose registers\n");
 | 
			
		||||
  }
 | 
			
		||||
  memset(&fp_regs, 0, sizeof(fp_regs));
 | 
			
		||||
  if (threads->GetFPRegisters(thread_info.pid, &fp_regs)) {
 | 
			
		||||
    printf("\n Floating point registers:\n");
 | 
			
		||||
    printf("  fctl                         = 0x%lx\n", fp_regs.cwd);
 | 
			
		||||
    printf("  fstat                        = 0x%lx\n", fp_regs.swd);
 | 
			
		||||
    printf("  ftag                         = 0x%lx\n", fp_regs.twd);
 | 
			
		||||
    printf("  fioff                        = 0x%lx\n", fp_regs.fip);
 | 
			
		||||
    printf("  fiseg                        = 0x%lx\n", fp_regs.fcs);
 | 
			
		||||
    printf("  fooff                        = 0x%lx\n", fp_regs.foo);
 | 
			
		||||
    printf("  foseg                        = 0x%lx\n", fp_regs.fos);
 | 
			
		||||
    int st_space_size = sizeof(fp_regs.st_space) / sizeof(fp_regs.st_space[0]);
 | 
			
		||||
    printf("  st_space[%2d]                 = 0x", st_space_size);
 | 
			
		||||
    for (int i = 0; i < st_space_size; ++i)
 | 
			
		||||
      printf("%02lx", fp_regs.st_space[i]);
 | 
			
		||||
    printf("\n");
 | 
			
		||||
  } else {
 | 
			
		||||
    fprintf(stderr, "ERROR: Failed to get floating-point registers\n");
 | 
			
		||||
  }
 | 
			
		||||
  memset(&fpx_regs, 0, sizeof(fpx_regs));
 | 
			
		||||
  if (threads->GetFPXRegisters(thread_info.pid, &fpx_regs)) {
 | 
			
		||||
    printf("\n Extended floating point registers:\n");
 | 
			
		||||
    printf("  fctl                         = 0x%x\n", fpx_regs.cwd);
 | 
			
		||||
    printf("  fstat                        = 0x%x\n", fpx_regs.swd);
 | 
			
		||||
    printf("  ftag                         = 0x%x\n", fpx_regs.twd);
 | 
			
		||||
    printf("  fioff                        = 0x%lx\n", fpx_regs.fip);
 | 
			
		||||
    printf("  fiseg                        = 0x%lx\n", fpx_regs.fcs);
 | 
			
		||||
    printf("  fooff                        = 0x%lx\n", fpx_regs.foo);
 | 
			
		||||
    printf("  foseg                        = 0x%lx\n", fpx_regs.fos);
 | 
			
		||||
    printf("  fop                          = 0x%x\n", fpx_regs.fop);
 | 
			
		||||
    printf("  mxcsr                        = 0x%lx\n", fpx_regs.mxcsr);
 | 
			
		||||
    int space_size = sizeof(fpx_regs.st_space) / sizeof(fpx_regs.st_space[0]);
 | 
			
		||||
    printf("  st_space[%2d]                 = 0x", space_size);
 | 
			
		||||
    for (int i = 0; i < space_size; ++i)
 | 
			
		||||
      printf("%02lx", fpx_regs.st_space[i]);
 | 
			
		||||
    printf("\n");
 | 
			
		||||
    space_size = sizeof(fpx_regs.xmm_space) / sizeof(fpx_regs.xmm_space[0]);
 | 
			
		||||
    printf("  xmm_space[%2d]                = 0x", space_size);
 | 
			
		||||
    for (int i = 0; i < space_size; ++i)
 | 
			
		||||
      printf("%02lx", fpx_regs.xmm_space[i]);
 | 
			
		||||
    printf("\n");
 | 
			
		||||
  }
 | 
			
		||||
  if (threads->GetDebugRegisters(thread_info.pid, &dbg_regs)) {
 | 
			
		||||
    printf("\n Debug registers:\n");
 | 
			
		||||
    printf("  dr0                          = 0x%x\n", dbg_regs.dr0);
 | 
			
		||||
    printf("  dr1                          = 0x%x\n", dbg_regs.dr1);
 | 
			
		||||
    printf("  dr2                          = 0x%x\n", dbg_regs.dr2);
 | 
			
		||||
    printf("  dr3                          = 0x%x\n", dbg_regs.dr3);
 | 
			
		||||
    printf("  dr4                          = 0x%x\n", dbg_regs.dr4);
 | 
			
		||||
    printf("  dr5                          = 0x%x\n", dbg_regs.dr5);
 | 
			
		||||
    printf("  dr6                          = 0x%x\n", dbg_regs.dr6);
 | 
			
		||||
    printf("  dr7                          = 0x%x\n", dbg_regs.dr7);
 | 
			
		||||
    printf("\n");
 | 
			
		||||
  }
 | 
			
		||||
  if (regs.esp != 0) {
 | 
			
		||||
    // Print the stack content.
 | 
			
		||||
    int size = 1024 * 2;
 | 
			
		||||
    char *buf = new char[size];
 | 
			
		||||
    size = threads->GetThreadStackDump(regs.ebp,
 | 
			
		||||
                                       regs.esp,
 | 
			
		||||
                                      (void*)buf, size);
 | 
			
		||||
    printf(" Stack content:                 = 0x");
 | 
			
		||||
    size /= sizeof(unsigned long);
 | 
			
		||||
    unsigned long *p_buf = (unsigned long *)(buf);
 | 
			
		||||
    for (int i = 0; i < size; i += 1)
 | 
			
		||||
      printf("%.8lx ", p_buf[i]);
 | 
			
		||||
    delete []buf;
 | 
			
		||||
    printf("\n");
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int PrintAllThreads(void *argument) {
 | 
			
		||||
  int pid = (int)argument;
 | 
			
		||||
 | 
			
		||||
  LinuxThread threads(pid);
 | 
			
		||||
  int total_thread = threads.SuspendAllThreads();
 | 
			
		||||
  printf("There are %d threads in the process: %d\n", total_thread, pid);
 | 
			
		||||
  int total_module = threads.GetModuleCount();
 | 
			
		||||
  printf("There are %d modules in the process: %d\n", total_module, pid);
 | 
			
		||||
  CallbackParam<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, ®s)) {
 | 
			
		||||
    uintptr_t last_ebp = regs.ebp;
 | 
			
		||||
    uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp);
 | 
			
		||||
    if (stack_bottom > last_ebp &&
 | 
			
		||||
        stack_bottom == crashing_context->crashing_stack_bottom) {
 | 
			
		||||
      // Got it. Stop iteration.
 | 
			
		||||
      crashing_context->crashing_thread_pid = thread_info.pid;
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find the crashing thread id.
 | 
			
		||||
// This is done based on stack bottom comparing.
 | 
			
		||||
int FindCrashingThread(uintptr_t crashing_stack_bottom,
 | 
			
		||||
                       int requester_pid,
 | 
			
		||||
                       const LinuxThread *thread_lister) {
 | 
			
		||||
  FindCrashThreadContext context;
 | 
			
		||||
  context.thread_lister = thread_lister;
 | 
			
		||||
  context.crashing_stack_bottom = crashing_stack_bottom;
 | 
			
		||||
  CallbackParam<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(®s, 0, sizeof(regs));
 | 
			
		||||
  if (!thread_lister->GetRegisters(thread_info.pid, ®s)) {
 | 
			
		||||
    perror(NULL);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  UntypedMDRVA memory(minidump_writer);
 | 
			
		||||
  if (!WriteThreadStack(regs.ebp,
 | 
			
		||||
                   regs.esp,
 | 
			
		||||
                   thread_lister,
 | 
			
		||||
                   &memory,
 | 
			
		||||
                   &thread->stack))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  struct user_fpregs_struct fp_regs;
 | 
			
		||||
  DebugRegs dbg_regs;
 | 
			
		||||
  memset(&fp_regs, 0, sizeof(fp_regs));
 | 
			
		||||
  // Get all the registers.
 | 
			
		||||
  thread_lister->GetFPRegisters(thread_info.pid, &fp_regs);
 | 
			
		||||
  thread_lister->GetDebugRegisters(thread_info.pid, &dbg_regs);
 | 
			
		||||
 | 
			
		||||
  // Write context
 | 
			
		||||
  TypedMDRVA<MDRawContextX86> context(minidump_writer);
 | 
			
		||||
  if (!context.Allocate())
 | 
			
		||||
    return false;
 | 
			
		||||
  thread->thread_context = context.location();
 | 
			
		||||
  memset(context.get(), 0, sizeof(MDRawContextX86));
 | 
			
		||||
  return WriteContext(context.get(), ®s, &fp_regs, &dbg_regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WriteCPUInformation(MDRawSystemInfo *sys_info) {
 | 
			
		||||
  const char *proc_cpu_path = "/proc/cpuinfo";
 | 
			
		||||
  char line[128];
 | 
			
		||||
  char vendor_id[13];
 | 
			
		||||
  const char vendor_id_name[] = "vendor_id";
 | 
			
		||||
  const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
 | 
			
		||||
 | 
			
		||||
  struct CpuInfoEntry {
 | 
			
		||||
    const char *info_name;
 | 
			
		||||
    int value;
 | 
			
		||||
  } cpu_info_table[] = {
 | 
			
		||||
    { "processor", -1 },
 | 
			
		||||
    { "model", 0 },
 | 
			
		||||
    { "stepping",  0 },
 | 
			
		||||
    { "cpuid level", 0 },
 | 
			
		||||
    { NULL, -1 },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  memset(vendor_id, 0, sizeof(vendor_id));
 | 
			
		||||
 | 
			
		||||
  FILE *fp = fopen(proc_cpu_path, "r");
 | 
			
		||||
  if (fp != NULL) {
 | 
			
		||||
    while (fgets(line, sizeof(line), fp)) {
 | 
			
		||||
      CpuInfoEntry *entry = &cpu_info_table[0];
 | 
			
		||||
      while (entry->info_name != NULL) {
 | 
			
		||||
        if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
 | 
			
		||||
          char *value = strchr(line, ':');
 | 
			
		||||
          value++;
 | 
			
		||||
          if (value != NULL)
 | 
			
		||||
            sscanf(value, " %d", &(entry->value));
 | 
			
		||||
        }
 | 
			
		||||
        entry++;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // special case for vendor_id
 | 
			
		||||
      if (!strncmp(line, vendor_id_name, vendor_id_name_length)) {
 | 
			
		||||
        char *value = strchr(line, ':');
 | 
			
		||||
        if (value == NULL)
 | 
			
		||||
          continue;
 | 
			
		||||
 | 
			
		||||
        value++;
 | 
			
		||||
        while (*value && isspace(*value))
 | 
			
		||||
          value++;
 | 
			
		||||
        if (*value) {
 | 
			
		||||
          size_t length = strlen(value);
 | 
			
		||||
          // we don't want the trailing newline
 | 
			
		||||
          if (value[length - 1] == '\n')
 | 
			
		||||
            length--;
 | 
			
		||||
          // ensure we have space for the value
 | 
			
		||||
          if (length < sizeof(vendor_id))
 | 
			
		||||
            strncpy(vendor_id, value, length);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    fclose(fp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // /proc/cpuinfo contains cpu id, change it into number by adding one.
 | 
			
		||||
  cpu_info_table[0].value++;
 | 
			
		||||
 | 
			
		||||
  sys_info->number_of_processors = cpu_info_table[0].value;
 | 
			
		||||
  sys_info->processor_level      = cpu_info_table[3].value;
 | 
			
		||||
  sys_info->processor_revision   = cpu_info_table[1].value << 8 |
 | 
			
		||||
                                   cpu_info_table[2].value;
 | 
			
		||||
 | 
			
		||||
  sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
 | 
			
		||||
  struct utsname uts;
 | 
			
		||||
  if (uname(&uts) == 0) {
 | 
			
		||||
    // Match i*86 and x86* as X86 architecture.
 | 
			
		||||
    if ((strstr(uts.machine, "x86") == uts.machine) ||
 | 
			
		||||
        (strlen(uts.machine) == 4 &&
 | 
			
		||||
         uts.machine[0] == 'i' &&
 | 
			
		||||
         uts.machine[2] == '8' &&
 | 
			
		||||
         uts.machine[3] == '6')) {
 | 
			
		||||
      sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86;
 | 
			
		||||
      if (vendor_id[0] != '\0')
 | 
			
		||||
        memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
 | 
			
		||||
               sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WriteOSInformation(MinidumpFileWriter *minidump_writer,
 | 
			
		||||
                        MDRawSystemInfo *sys_info) {
 | 
			
		||||
  sys_info->platform_id = MD_OS_LINUX;
 | 
			
		||||
 | 
			
		||||
  struct utsname uts;
 | 
			
		||||
  if (uname(&uts) == 0) {
 | 
			
		||||
    char os_version[512];
 | 
			
		||||
    size_t space_left = sizeof(os_version);
 | 
			
		||||
    memset(os_version, 0, space_left);
 | 
			
		||||
    const char *os_info_table[] = {
 | 
			
		||||
      uts.sysname,
 | 
			
		||||
      uts.release,
 | 
			
		||||
      uts.version,
 | 
			
		||||
      uts.machine,
 | 
			
		||||
      "GNU/Linux",
 | 
			
		||||
      NULL
 | 
			
		||||
    };
 | 
			
		||||
    for (const char **cur_os_info = os_info_table;
 | 
			
		||||
         *cur_os_info != NULL;
 | 
			
		||||
         cur_os_info++) {
 | 
			
		||||
      if (cur_os_info != os_info_table && space_left > 1) {
 | 
			
		||||
        strcat(os_version, " ");
 | 
			
		||||
        space_left--;
 | 
			
		||||
      }
 | 
			
		||||
      if (space_left > strlen(*cur_os_info)) {
 | 
			
		||||
        strcat(os_version, *cur_os_info);
 | 
			
		||||
        space_left -= strlen(*cur_os_info);
 | 
			
		||||
      } else {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MDLocationDescriptor location;
 | 
			
		||||
    if (!minidump_writer->WriteString(os_version, 0, &location))
 | 
			
		||||
      return false;
 | 
			
		||||
    sys_info->csd_version_rva = location.rva;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Callback context for get writting thread information.
 | 
			
		||||
struct ThreadInfoCallbackCtx {
 | 
			
		||||
  MinidumpFileWriter *minidump_writer;
 | 
			
		||||
  const WriterArgument *writer_args;
 | 
			
		||||
  TypedMDRVA<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
 | 
			
		||||
							
								
								
									
										105
									
								
								src/client/linux/minidump_writer/directory_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/client/linux/minidump_writer/directory_reader.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										130
									
								
								src/client/linux/minidump_writer/line_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/client/linux/minidump_writer/line_reader.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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_
 | 
			
		||||
							
								
								
									
										184
									
								
								src/client/linux/minidump_writer/line_reader_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/client/linux/minidump_writer/line_reader_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										419
									
								
								src/client/linux/minidump_writer/linux_dumper.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								src/client/linux/minidump_writer/linux_dumper.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,419 @@
 | 
			
		|||
// 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) || defined(__x86_64)
 | 
			
		||||
  if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										142
									
								
								src/client/linux/minidump_writer/linux_dumper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/client/linux/minidump_writer/linux_dumper.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,142 @@
 | 
			
		|||
// 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 <stdint.h>
 | 
			
		||||
#include <sys/user.h>
 | 
			
		||||
#include <linux/limits.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) || defined(__x86_64)
 | 
			
		||||
  user_fpxregs_struct fpxregs;
 | 
			
		||||
 | 
			
		||||
  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_
 | 
			
		||||
							
								
								
									
										118
									
								
								src/client/linux/minidump_writer/linux_dumper_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/client/linux/minidump_writer/linux_dumper_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										872
									
								
								src/client/linux/minidump_writer/minidump_writer.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										872
									
								
								src/client/linux/minidump_writer/minidump_writer.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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.twd;
 | 
			
		||||
  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.mxcsr_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->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->mxcsr_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, sizeof(MDRawModule)))
 | 
			
		||||
      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, sizeof(mod));
 | 
			
		||||
      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, sizeof(mod));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 },
 | 
			
		||||
      { "cpuid level", 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]);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,20 +27,22 @@
 | 
			
		|||
// (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 <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 <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>
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +123,7 @@ 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;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 = §ions[i];
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (text_section != NULL && text_section->sh_size > 0) {
 | 
			
		||||
    int text_section_size = text_section->sh_size;
 | 
			
		||||
    *text_start = elf_base + text_section->sh_offset;
 | 
			
		||||
    *text_size = text_section_size;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FileID::FileID(const char *path) {
 | 
			
		||||
FileID::FileID(const char* path) {
 | 
			
		||||
  strncpy(path_, path, sizeof(path_));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FileID::ElfFileIdentifier(unsigned char identifier[16]) {
 | 
			
		||||
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
 | 
			
		||||
  const ssize_t mapped_len = 4096;  // Page size (matches WriteMappings())
 | 
			
		||||
  int fd = open(path_, O_RDONLY);
 | 
			
		||||
  if (fd < 0)
 | 
			
		||||
    return false;
 | 
			
		||||
  struct stat st;
 | 
			
		||||
  if (fstat(fd, &st) != 0 && st.st_size <= 0) {
 | 
			
		||||
  if (fstat(fd, &st) != 0 || st.st_size <= mapped_len) {
 | 
			
		||||
    close(fd);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  void *base = mmap(NULL, st.st_size,
 | 
			
		||||
  void* base = mmap(NULL, mapped_len,
 | 
			
		||||
                    PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
 | 
			
		||||
  if (base == MAP_FAILED) {
 | 
			
		||||
    close(fd);
 | 
			
		||||
  close(fd);
 | 
			
		||||
  if (base == MAP_FAILED)
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  bool success = false;
 | 
			
		||||
  const void *text_section = NULL;
 | 
			
		||||
  int text_size = 0;
 | 
			
		||||
  if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) {
 | 
			
		||||
    struct MD5Context md5;
 | 
			
		||||
    MD5Init(&md5);
 | 
			
		||||
    MD5Update(&md5,
 | 
			
		||||
              static_cast<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__
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										178
									
								
								src/common/linux/linux_libc_support.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								src/common/linux/linux_libc_support.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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_
 | 
			
		||||
							
								
								
									
										153
									
								
								src/common/linux/linux_libc_support_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/common/linux/linux_libc_support_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 "breakpad/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, '-');
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2800
									
								
								src/common/linux/linux_syscall_support.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2800
									
								
								src/common/linux/linux_syscall_support.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										176
									
								
								src/common/linux/memory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/common/linux/memory.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,176 @@
 | 
			
		|||
// 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) {
 | 
			
		||||
    void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
 | 
			
		||||
                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 | 
			
		||||
    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_
 | 
			
		||||
							
								
								
									
										84
									
								
								src/common/linux/memory_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/common/linux/memory_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 "breakpad/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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										602
									
								
								src/tools/linux/md2core/minidump-2-core.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										602
									
								
								src/tools/linux/md2core/minidump-2-core.cc
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,602 @@
 | 
			
		|||
// 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 "breakpad/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),
 | 
			
		||||
                                             sizeof(MDRawModule), 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;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in a new issue