Add Linux exception handler.
Add Linux stab symbol dumper. Add minidump & symbol uploader for Linux. git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@126 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
1f3d2571d1
commit
bcd46f0079
23 changed files with 4032 additions and 0 deletions
51
src/client/linux/handler/Makefile
Normal file
51
src/client/linux/handler/Makefile
Normal file
|
@ -0,0 +1,51 @@
|
|||
CC=g++
|
||||
|
||||
CPPFLAGS=-gstabs -I../../.. -Wall -DNDEBUG -D_REENTRANT
|
||||
LDFLAGS=-lpthread -lssl
|
||||
|
||||
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
|
||||
|
||||
THREAD_TEST_SRC=linux_thread_test.cc
|
||||
MINIDUMP_TEST_SRC=minidump_test.cc
|
||||
EXCEPTION_TEST_SRC=exception_handler_test.cc
|
||||
|
||||
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))
|
||||
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_SRC)
|
||||
|
||||
BIN=$(BIN_DIR)/minidump_test\
|
||||
$(BIN_DIR)/linux_thread_test\
|
||||
$(BIN_DIR)/exception_handler_test
|
||||
|
||||
.PHONY:all clean
|
||||
|
||||
all:$(BIN)
|
||||
|
||||
$(BIN_DIR)/linux_thread_test:$(THREAD_TEST_OBJ)
|
||||
$(CC) $(CPPFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
$(BIN_DIR)/minidump_test:$(MINIDUMP_TEST_OBJ)
|
||||
$(CC) $(CPPFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
$(BIN_DIR)/exception_handler_test:$(EXCEPTION_TEST_OBJ)
|
||||
$(CC) $(CPPFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(BIN) *.o *.dmp
|
236
src/client/linux/handler/exception_handler.cc
Normal file
236
src/client/linux/handler/exception_handler.cc
Normal file
|
@ -0,0 +1,236 @@
|
|||
// 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 <asm/sigcontext.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
std::vector<ExceptionHandler*> *ExceptionHandler::handler_stack_ = NULL;
|
||||
int ExceptionHandler::handler_stack_index_ = 0;
|
||||
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
|
||||
PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
ExceptionHandler::ExceptionHandler(const string &dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler)
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
dump_path_(),
|
||||
installed_handler_(install_handler) {
|
||||
set_dump_path(dump_path);
|
||||
|
||||
if (install_handler) {
|
||||
SetupHandler();
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
if (handler_stack_ == NULL)
|
||||
handler_stack_ = new std::vector<ExceptionHandler *>;
|
||||
handler_stack_->push_back(this);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
}
|
||||
}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
return InternalWriteMinidump(0, NULL);
|
||||
}
|
||||
|
||||
// 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, NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
struct sigaction act, old_act;
|
||||
act.sa_handler = HandleException;
|
||||
act.sa_flags = SA_ONSTACK;
|
||||
if (sigaction(signo, &act, &old_act) < 0)
|
||||
return;
|
||||
old_handlers_[signo] = old_act.sa_handler;
|
||||
}
|
||||
|
||||
void ExceptionHandler::TeardownHandler(int signo) {
|
||||
if (old_handlers_.find(signo) != old_handlers_.end()) {
|
||||
struct sigaction act;
|
||||
act.sa_handler = old_handlers_[signo];
|
||||
act.sa_flags = 0;
|
||||
sigaction(signo, &act, 0);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
const struct sigcontext *sig_ctx =
|
||||
reinterpret_cast<const struct sigcontext *>(&signo + 1);
|
||||
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.
|
||||
current_handler->TeardownHandler(signo);
|
||||
if (current_handler->InternalWriteMinidump(signo, 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.
|
||||
typedef void (*SignalHandler)(int signo, struct sigcontext);
|
||||
SignalHandler old_handler =
|
||||
reinterpret_cast<SignalHandler>(current_handler->old_handlers_[signo]);
|
||||
if (old_handler != NULL)
|
||||
old_handler(signo, *sig_ctx);
|
||||
}
|
||||
|
||||
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,
|
||||
const struct sigcontext *sig_ctx) {
|
||||
if (filter_ && !filter_(callback_context_))
|
||||
return false;
|
||||
|
||||
GUID guid;
|
||||
bool success = false;;
|
||||
char guid_str[kGUIDStringLength + 1];
|
||||
if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
|
||||
char minidump_path[PATH_MAX];
|
||||
snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
|
||||
dump_path_c_,
|
||||
guid_str);
|
||||
success = minidump_generator_.WriteMinidumpToFile(
|
||||
minidump_path, signo, sig_ctx);
|
||||
if (callback_)
|
||||
success = callback_(dump_path_c_, guid_str,
|
||||
callback_context_, success);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
194
src/client/linux/handler/exception_handler.h
Normal file
194
src/client/linux/handler/exception_handler.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
// 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_EXCEPTION_HANDLER_H__
|
||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "client/linux/handler/minidump_generator.h"
|
||||
|
||||
// Context information when exception occured.
|
||||
struct sigcontex;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
//
|
||||
// ExceptionHandler
|
||||
//
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
// or when WriteMinidump() is called explicitly by your program.
|
||||
//
|
||||
// To have the exception handler write minidumps when an uncaught exception
|
||||
// (crash) occurs, you should create an instance early in the execution
|
||||
// of your program, and keep it around for the entire time you want to
|
||||
// have crash handling active (typically, until shutdown).
|
||||
// (NOTE): There should be only be one this kind of exception handler
|
||||
// object per process.
|
||||
//
|
||||
// If you want to write minidumps without installing the exception handler,
|
||||
// you can create an ExceptionHandler with install_handler set to false,
|
||||
// then call WriteMinidump. You can also use this technique if you want to
|
||||
// use different minidump callbacks for different call sites.
|
||||
//
|
||||
// In either case, a callback function is called when a minidump is written,
|
||||
// which receives the unqiue id of the minidump. The caller can use this
|
||||
// id to collect and write additional application state, and to launch an
|
||||
// external crash-reporting application.
|
||||
//
|
||||
// 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
|
||||
// processing of an exception. A FilterCallback is called before writing
|
||||
// a minidump. context is the parameter supplied by the user as
|
||||
// callback_context when the handler was created.
|
||||
//
|
||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
||||
// attempting to write a minidump. If a FilterCallback returns false,
|
||||
// Breakpad will immediately report the exception as unhandled without
|
||||
// writing a minidump, allowing another handler the opportunity to handle it.
|
||||
typedef bool (*FilterCallback)(void *context);
|
||||
|
||||
// A callback function to run after the minidump has been written.
|
||||
// minidump_id is a unique id for the dump, so the minidump
|
||||
// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
|
||||
// by the user as callback_context when the handler was created. succeeded
|
||||
// indicates whether a minidump file was successfully written.
|
||||
//
|
||||
// If an exception occurred and the callback returns true, Breakpad will
|
||||
// treat the exception as fully-handled, suppressing any other handlers from
|
||||
// being notified of the exception. If the callback returns false, Breakpad
|
||||
// will treat the exception as unhandled, and allow another handler to handle
|
||||
// it. If there are no other handlers, Breakpad will report the exception to
|
||||
// the system as unhandled, allowing a debugger or native crash dialog the
|
||||
// opportunity to handle the exception. Most callback implementations
|
||||
// should normally return the value of |succeeded|, or when they wish to
|
||||
// not report an exception of handled, false. Callbacks will rarely want to
|
||||
// return true directly (unless |succeeded| is true).
|
||||
typedef bool (*MinidumpCallback)(const char *dump_path,
|
||||
const char *minidump_id,
|
||||
void *context,
|
||||
bool succeeded);
|
||||
|
||||
// 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
|
||||
// minidump. Minidump files will be written to dump_path, and the optional
|
||||
// callback is called after writing the dump file, as described above.
|
||||
// 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,
|
||||
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) {
|
||||
dump_path_ = dump_path;
|
||||
dump_path_c_ = dump_path_.c_str();
|
||||
}
|
||||
|
||||
// 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,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context);
|
||||
|
||||
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 all handlers.
|
||||
void TeardownAllHandler();
|
||||
|
||||
// Signal handler.
|
||||
static void HandleException(int signo);
|
||||
|
||||
bool InternalWriteMinidump(int signo, const struct sigcontext *sig_ctx);
|
||||
|
||||
private:
|
||||
FilterCallback filter_;
|
||||
MinidumpCallback callback_;
|
||||
void *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_;
|
||||
// C style dump path. Keep this when setting dump path, since calling
|
||||
// c_str() of std::string when crashing may not be safe.
|
||||
const char *dump_path_c_;
|
||||
|
||||
// True if the ExceptionHandler installed an unhandled exception filter
|
||||
// when created (with an install_handler parameter set to true).
|
||||
bool installed_handler_;
|
||||
|
||||
// Keep the previous handlers for the signal.
|
||||
typedef void (*sighandler_t)(int);
|
||||
std::map<int, sighandler_t> old_handlers_;
|
||||
|
||||
// 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_;
|
||||
// The index of the handler that should handle the next exception.
|
||||
static int 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 &);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
|
124
src/client/linux/handler/exception_handler_test.cc
Normal file
124
src/client/linux/handler/exception_handler_test.cc
Normal file
|
@ -0,0 +1,124 @@
|
|||
// 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 = 1;
|
||||
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;
|
||||
}
|
384
src/client/linux/handler/linux_thread.cc
Normal file
384
src/client/linux/handler/linux_thread.cc
Normal file
|
@ -0,0 +1,384 @@
|
|||
// 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 <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];
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
201
src/client/linux/handler/linux_thread.h
Normal file
201
src/client/linux/handler/linux_thread.h
Normal file
|
@ -0,0 +1,201 @@
|
|||
// 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_esp) const;
|
||||
|
||||
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__
|
224
src/client/linux/handler/linux_thread_test.cc
Normal file
224
src/client/linux/handler/linux_thread_test.cc
Normal file
|
@ -0,0 +1,224 @@
|
|||
// 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;
|
||||
}
|
766
src/client/linux/handler/minidump_generator.cc
Normal file
766
src/client/linux/handler/minidump_generator.cc
Normal file
|
@ -0,0 +1,766 @@
|
|||
// 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 <asm/sigcontext.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
#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"
|
||||
|
||||
// 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;
|
||||
|
||||
// Signal contex when crash happed. Can be NULL if this is a requested dump.
|
||||
const 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) {
|
||||
char *proc_cpu_path = "/proc/cpuinfo";
|
||||
char line[128];
|
||||
|
||||
struct CpuInfoEntry {
|
||||
char *info_name;
|
||||
int value;
|
||||
} cpu_info_table[] = {
|
||||
{ "processor", -1 },
|
||||
{ "model", 0 },
|
||||
{ "stepping", 0 },
|
||||
{ "cpuid level", 0 },
|
||||
{ NULL, -1 },
|
||||
};
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
char *os_info_table[] = {
|
||||
uts.sysname,
|
||||
uts.release,
|
||||
uts.version,
|
||||
uts.machine,
|
||||
"GNU/Linux",
|
||||
NULL
|
||||
};
|
||||
for (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);
|
||||
return thread_lister->ListThreads(&callback_param) == 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
|
||||
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, module_name, module_name_length))
|
||||
return false;
|
||||
|
||||
module->cv_record = cv.location();
|
||||
MDCVInfoPDB70 *cv_ptr = cv.get();
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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->sig_ctx != NULL) {
|
||||
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,
|
||||
const 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.sig_ctx = sig_ctx;
|
||||
|
||||
int cloned_pid = clone(Write, stack_.get() + kStackSize,
|
||||
CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
(void*)&argument);
|
||||
waitpid(cloned_pid, NULL, __WALL);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
70
src/client/linux/handler/minidump_generator.h
Normal file
70
src/client/linux/handler/minidump_generator.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// 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_MINIDUMP_GENERATOR_H__
|
||||
#define CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
struct sigcontext;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//
|
||||
// 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,
|
||||
const 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_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__
|
86
src/client/linux/handler/minidump_test.cc
Normal file
86
src/client/linux/handler/minidump_test.cc
Normal file
|
@ -0,0 +1,86 @@
|
|||
// 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/minidump_generator.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
// Thread use this to see if it should stop working.
|
||||
static bool should_exit = false;
|
||||
|
||||
static void foo2(int arg) {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
int c = arg;
|
||||
c = 0xcccccccc;
|
||||
while (!should_exit)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
static void foo(int arg) {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
int b = arg;
|
||||
b = 0xbbbbbbbb;
|
||||
foo2(b);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
CreateThread(10);
|
||||
google_breakpad::MinidumpGenerator mg;
|
||||
if (mg.WriteMinidumpToFile("minidump_test.out", -1, NULL))
|
||||
printf("Succeeded written minidump\n");
|
||||
else
|
||||
printf("Failed to write minidump\n");
|
||||
should_exit = true;
|
||||
return 0;
|
||||
}
|
613
src/common/linux/dump_symbols.cc
Normal file
613
src/common/linux/dump_symbols.cc
Normal file
|
@ -0,0 +1,613 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <a.out.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <cxxabi.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <link.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stab.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/dump_symbols.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
// This namespace contains helper functions.
|
||||
namespace {
|
||||
|
||||
// Infomation of a line.
|
||||
struct LineInfo {
|
||||
// Offset from start of the function.
|
||||
// Load from stab symbol.
|
||||
ElfW(Off) rva_to_func;
|
||||
// Offset from base of the loading binary.
|
||||
ElfW(Off) rva_to_base;
|
||||
// Size of the line.
|
||||
// It is the difference of the starting address of the line and starting
|
||||
// address of the next N_SLINE, N_FUN or N_SO.
|
||||
uint32_t size;
|
||||
// Line number.
|
||||
uint32_t line_num;
|
||||
};
|
||||
|
||||
// Information of a function.
|
||||
struct FuncInfo {
|
||||
// Name of the function.
|
||||
const char *name;
|
||||
// Offset from the base of the loading address.
|
||||
ElfW(Off) rva_to_base;
|
||||
// Virtual address of the function.
|
||||
// Load from stab symbol.
|
||||
ElfW(Addr) addr;
|
||||
// Size of the function.
|
||||
// It is the difference of the starting address of the function and starting
|
||||
// address of the next N_FUN or N_SO.
|
||||
uint32_t size;
|
||||
// Total size of stack parameters.
|
||||
uint32_t stack_param_size;
|
||||
// Is the function defined in included function?
|
||||
bool is_sol;
|
||||
// Line information array.
|
||||
std::vector<struct LineInfo> line_info;
|
||||
};
|
||||
|
||||
// Information of a source file.
|
||||
struct SourceFileInfo {
|
||||
// Name of the source file.
|
||||
const char *name;
|
||||
// Starting address of the source file.
|
||||
ElfW(Addr) addr;
|
||||
// Id of the source file.
|
||||
int source_id;
|
||||
// Functions information.
|
||||
std::vector<struct FuncInfo> func_info;
|
||||
};
|
||||
|
||||
// Information of a symbol table.
|
||||
// This is the root of all types of symbol.
|
||||
struct SymbolInfo {
|
||||
std::vector<struct SourceFileInfo> source_file_info;
|
||||
};
|
||||
|
||||
// Stab section name.
|
||||
const char *kStabName = ".stab";
|
||||
|
||||
// Stab str section name.
|
||||
const char *kStabStrName = ".stabstr";
|
||||
|
||||
// Demangle using abi call.
|
||||
// Older GCC may not support it.
|
||||
std::string Demangle(const char *mangled) {
|
||||
int status = 0;
|
||||
char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status);
|
||||
if (status == 0 && demangled != NULL) {
|
||||
std::string str(demangled);
|
||||
free(demangled);
|
||||
return str;
|
||||
}
|
||||
return std::string(mangled);
|
||||
}
|
||||
|
||||
// Fix offset into virtual address by adding the mapped base into offsets.
|
||||
// Make life easier when want to find something by offset.
|
||||
void FixAddress(void *obj_base) {
|
||||
ElfW(Word) base = reinterpret_cast<ElfW(Word)>(obj_base);
|
||||
ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(obj_base);
|
||||
elf_header->e_phoff += base;
|
||||
elf_header->e_shoff += base;
|
||||
ElfW(Shdr) *sections = reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
for (int i = 0; i < elf_header->e_shnum; ++i)
|
||||
sections[i].sh_offset += base;
|
||||
}
|
||||
|
||||
// Find the prefered loading address of the binary.
|
||||
// It is the PT_LOAD segment with offset to zero.
|
||||
ElfW(Addr) GetLoadingAddress(const ElfW(Phdr) *program_headers, int nheader) {
|
||||
for (int i = 0; i < nheader; ++i) {
|
||||
const ElfW(Phdr) &header = program_headers[i];
|
||||
if (header.p_type == PT_LOAD &&
|
||||
header.p_offset == 0)
|
||||
return header.p_vaddr;
|
||||
}
|
||||
assert(!"Should get a valid loading address");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool WriteFormat(int fd, const char *fmt, ...) {
|
||||
va_list list;
|
||||
char buffer[4096];
|
||||
ssize_t expected, written;
|
||||
va_start(list, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, list);
|
||||
expected = strlen(buffer);
|
||||
written = write(fd, buffer, strlen(buffer));
|
||||
va_end(list);
|
||||
return expected == written;
|
||||
}
|
||||
|
||||
bool IsValidElf(const ElfW(Ehdr) *elf_header) {
|
||||
return memcmp(elf_header, ELFMAG, SELFMAG) == 0;
|
||||
}
|
||||
|
||||
const ElfW(Shdr) *FindSectionByName(const char *name,
|
||||
const ElfW(Shdr) *sections,
|
||||
const ElfW(Shdr) *strtab,
|
||||
int nsection) {
|
||||
assert(name != NULL);
|
||||
assert(sections != NULL);
|
||||
assert(nsection > 0);
|
||||
|
||||
int name_len = strlen(name);
|
||||
if (name_len == 0)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < nsection; ++i) {
|
||||
const char *section_name =
|
||||
(char*)(strtab->sh_offset + sections[i].sh_name);
|
||||
if (!strncmp(name, section_name, name_len))
|
||||
return sections + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO(liuli): Computer the stack parameter size.
|
||||
// Expect parameter variables are immediately following the N_FUN symbol.
|
||||
// Will need to parse the type information to get a correct size.
|
||||
int LoadStackParamSize(struct nlist *list,
|
||||
struct nlist *list_end,
|
||||
struct FuncInfo *func_info) {
|
||||
struct nlist *cur_list = list;
|
||||
assert(cur_list->n_type == N_FUN);
|
||||
++cur_list;
|
||||
int step = 1;
|
||||
while (cur_list < list_end && cur_list->n_type == N_PSYM) {
|
||||
++cur_list;
|
||||
++step;
|
||||
}
|
||||
func_info->stack_param_size = 0;
|
||||
return step;
|
||||
}
|
||||
|
||||
int LoadLineInfo(struct nlist *list,
|
||||
struct nlist *list_end,
|
||||
struct FuncInfo *func_info) {
|
||||
struct nlist *cur_list = list;
|
||||
func_info->is_sol = false;
|
||||
do {
|
||||
// Skip non line information.
|
||||
while (cur_list < list_end && cur_list->n_type != N_SLINE) {
|
||||
// Only exit when got another function, or source file.
|
||||
if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO)
|
||||
return cur_list - list;
|
||||
if (cur_list->n_type == N_SOL)
|
||||
func_info->is_sol = true;
|
||||
++cur_list;
|
||||
}
|
||||
struct LineInfo line;
|
||||
while (cur_list < list_end && cur_list->n_type == N_SLINE) {
|
||||
line.rva_to_func = cur_list->n_value;
|
||||
line.line_num = cur_list->n_desc;
|
||||
func_info->line_info.push_back(line);
|
||||
++cur_list;
|
||||
}
|
||||
} while (list < list_end);
|
||||
|
||||
return cur_list - list;
|
||||
}
|
||||
|
||||
int LoadFuncSymbols(struct nlist *list,
|
||||
struct nlist *list_end,
|
||||
const ElfW(Shdr) *stabstr_section,
|
||||
struct SourceFileInfo *source_file_info) {
|
||||
struct nlist *cur_list = list;
|
||||
assert(cur_list->n_type == N_SO);
|
||||
++cur_list;
|
||||
|
||||
source_file_info->func_info.clear();
|
||||
while (cur_list < list_end) {
|
||||
// Go until the function symbol.
|
||||
while (cur_list < list_end && cur_list->n_type != N_FUN) {
|
||||
if (cur_list->n_type == N_SO) {
|
||||
return cur_list - list;
|
||||
}
|
||||
++cur_list;
|
||||
continue;
|
||||
}
|
||||
if (cur_list->n_type == N_FUN) {
|
||||
struct FuncInfo func_info;
|
||||
memset(&func_info, 0, sizeof(func_info));
|
||||
func_info.name =
|
||||
reinterpret_cast<char *>(cur_list->n_un.n_strx +
|
||||
stabstr_section->sh_offset);
|
||||
func_info.addr = cur_list->n_value;
|
||||
// Stack parameter size.
|
||||
cur_list += LoadStackParamSize(cur_list, list_end, &func_info);
|
||||
// Line info.
|
||||
cur_list += LoadLineInfo(cur_list, list_end, &func_info);
|
||||
// Functions in this module should have address bigger than the module
|
||||
// startring address.
|
||||
if (func_info.addr >= source_file_info->addr) {
|
||||
source_file_info->func_info.push_back(func_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cur_list - list;
|
||||
}
|
||||
|
||||
// Comapre the address.
|
||||
// The argument should have a memeber named "addr"
|
||||
template<class T1, class T2>
|
||||
bool CompareAddress(T1 *a, T2 *b) {
|
||||
return a->addr < b->addr;
|
||||
}
|
||||
|
||||
// Sort the array into increasing ordered array based on the virtual address.
|
||||
// Return vector of pointers to the elements in the incoming array. So caller
|
||||
// should make sure the returned vector lives longer than the incoming vector.
|
||||
template<class T>
|
||||
std::vector<T *> SortByAddress(std::vector<T> *array) {
|
||||
std::vector<T *> sorted_array_ptr;
|
||||
sorted_array_ptr.reserve(array->size());
|
||||
for (size_t i = 0; i < array->size(); ++i)
|
||||
sorted_array_ptr.push_back(&(array->at(i)));
|
||||
std::sort(sorted_array_ptr.begin(),
|
||||
sorted_array_ptr.end(),
|
||||
std::ptr_fun(CompareAddress<T, T>));
|
||||
|
||||
return sorted_array_ptr;
|
||||
}
|
||||
|
||||
// Find the address of the next function or source file symbol in the symbol
|
||||
// table. The address should be bigger than the current function's address.
|
||||
ElfW(Addr) NextAddress(std::vector<struct FuncInfo *> *sorted_functions,
|
||||
std::vector<struct SourceFileInfo *> *sorted_files,
|
||||
const struct FuncInfo &func_info) {
|
||||
std::vector<struct FuncInfo *>::iterator next_func_iter =
|
||||
std::find_if(sorted_functions->begin(),
|
||||
sorted_functions->end(),
|
||||
std::bind1st(
|
||||
std::ptr_fun(
|
||||
CompareAddress<struct FuncInfo,
|
||||
struct FuncInfo>
|
||||
),
|
||||
&func_info)
|
||||
);
|
||||
if (next_func_iter != sorted_functions->end())
|
||||
return (*next_func_iter)->addr;
|
||||
|
||||
std::vector<struct SourceFileInfo *>::iterator next_file_iter =
|
||||
std::find_if(sorted_files->begin(),
|
||||
sorted_files->end(),
|
||||
std::bind1st(
|
||||
std::ptr_fun(
|
||||
CompareAddress<struct FuncInfo,
|
||||
struct SourceFileInfo>
|
||||
),
|
||||
&func_info)
|
||||
);
|
||||
if (next_file_iter != sorted_files->end()) {
|
||||
return (*next_file_iter)->addr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compute size and rva information based on symbols loaded from stab section.
|
||||
bool ComputeSizeAndRVA(ElfW(Addr) loading_addr, struct SymbolInfo *symbols) {
|
||||
std::vector<struct SourceFileInfo *> sorted_files =
|
||||
SortByAddress(&(symbols->source_file_info));
|
||||
for (size_t i = 0; i < sorted_files.size(); ++i) {
|
||||
struct SourceFileInfo &source_file = *sorted_files[i];
|
||||
std::vector<struct FuncInfo *> sorted_functions =
|
||||
SortByAddress(&(source_file.func_info));
|
||||
for (size_t j = 0; j < sorted_functions.size(); ++j) {
|
||||
struct FuncInfo &func_info = *sorted_functions[j];
|
||||
assert(func_info.addr >= loading_addr);
|
||||
func_info.rva_to_base = func_info.addr - loading_addr;
|
||||
func_info.size = 0;
|
||||
ElfW(Addr) next_addr = NextAddress(&sorted_functions,
|
||||
&sorted_files,
|
||||
func_info);
|
||||
if (next_addr == 0)
|
||||
continue;
|
||||
func_info.size = next_addr - func_info.addr;
|
||||
|
||||
// Compute line size.
|
||||
for (size_t k = 0; k < func_info.line_info.size(); ++k) {
|
||||
struct LineInfo &line_info = func_info.line_info[k];
|
||||
line_info.size = 0;
|
||||
if (k + 1 < func_info.line_info.size()) {
|
||||
line_info.size =
|
||||
func_info.line_info[k + 1].rva_to_func - line_info.rva_to_func;
|
||||
} else {
|
||||
if (next_addr != 0) {
|
||||
ElfW(Off) next_addr_offset = next_addr - func_info.addr;
|
||||
line_info.size = next_addr_offset - line_info.rva_to_func;
|
||||
}
|
||||
}
|
||||
line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base;
|
||||
} // for each line.
|
||||
} // for each function.
|
||||
} // for each source file.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadSymbols(const ElfW(Shdr) *stab_section,
|
||||
const ElfW(Shdr) *stabstr_section,
|
||||
ElfW(Addr) loading_addr,
|
||||
struct SymbolInfo *symbols) {
|
||||
if (stab_section == NULL || stabstr_section == NULL)
|
||||
return false;
|
||||
|
||||
struct nlist *lists =
|
||||
reinterpret_cast<struct nlist *>(stab_section->sh_offset);
|
||||
int nstab = stab_section->sh_size / sizeof(struct nlist);
|
||||
int source_id = 0;
|
||||
// First pass, load all symbols from the object file.
|
||||
for (int i = 0; i < nstab; ) {
|
||||
int step = 1;
|
||||
struct nlist *cur_list = lists + i;
|
||||
if (cur_list->n_type == N_SO) {
|
||||
// FUNC <address> <length> <param_stack_size> <function>
|
||||
struct SourceFileInfo source_file_info;
|
||||
source_file_info.name = reinterpret_cast<char *>(cur_list->n_un.n_strx +
|
||||
stabstr_section->sh_offset);
|
||||
source_file_info.addr = cur_list->n_value;
|
||||
if (strchr(source_file_info.name, '.'))
|
||||
source_file_info.source_id = source_id++;
|
||||
else
|
||||
source_file_info.source_id = -1;
|
||||
step = LoadFuncSymbols(cur_list, lists + nstab,
|
||||
stabstr_section, &source_file_info);
|
||||
symbols->source_file_info.push_back(source_file_info);
|
||||
}
|
||||
i += step;
|
||||
}
|
||||
// Second pass, compute the size of functions and lines.
|
||||
return ComputeSizeAndRVA(loading_addr, symbols);
|
||||
}
|
||||
|
||||
bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) {
|
||||
// Translate all offsets in section headers into address.
|
||||
FixAddress(elf_header);
|
||||
ElfW(Addr) loading_addr = GetLoadingAddress(
|
||||
reinterpret_cast<ElfW(Phdr) *>(elf_header->e_phoff),
|
||||
elf_header->e_phnum);
|
||||
if (loading_addr == 0)
|
||||
return false;
|
||||
|
||||
const ElfW(Shdr) *sections =
|
||||
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
const ElfW(Shdr) *strtab = sections + elf_header->e_shstrndx;
|
||||
const ElfW(Shdr) *stab_section =
|
||||
FindSectionByName(kStabName, sections, strtab, elf_header->e_shnum);
|
||||
if (stab_section == NULL) {
|
||||
fprintf(stderr, "Stab section not found.\n");
|
||||
return false;
|
||||
}
|
||||
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
|
||||
|
||||
// Load symbols.
|
||||
return LoadSymbols(stab_section, stabstr_section, loading_addr, symbols);
|
||||
}
|
||||
|
||||
bool WriteModuleInfo(int fd, ElfW(Half) arch, const std::string &obj_file) {
|
||||
const char *arch_name = NULL;
|
||||
if (arch == EM_386)
|
||||
arch_name = "x86";
|
||||
else if (arch == EM_X86_64)
|
||||
arch_name = "x86_64";
|
||||
else
|
||||
return false;
|
||||
|
||||
unsigned char identifier[16];
|
||||
google_breakpad::FileID file_id(obj_file.c_str());
|
||||
if (file_id.ElfFileIdentifier(identifier)) {
|
||||
char identifier_str[40];
|
||||
file_id.ConvertIdentifierToString(identifier,
|
||||
identifier_str, sizeof(identifier_str));
|
||||
char id_no_dash[40];
|
||||
int id_no_dash_len = 0;
|
||||
memset(id_no_dash, 0, sizeof(id_no_dash));
|
||||
for (int i = 0; identifier_str[i] != '\0'; ++i)
|
||||
if (identifier_str[i] != '-')
|
||||
id_no_dash[id_no_dash_len++] = identifier_str[i];
|
||||
// Add an extra "0" by the end.
|
||||
id_no_dash[id_no_dash_len++] = '0';
|
||||
std::string filename = obj_file;
|
||||
size_t slash_pos = obj_file.find_last_of("/");
|
||||
if (slash_pos != std::string::npos)
|
||||
filename = obj_file.substr(slash_pos + 1);
|
||||
return WriteFormat(fd, "MODULE Linux %s %s 1 %s\n", arch_name,
|
||||
id_no_dash, filename.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WriteSourceFileInfo(int fd, const struct SymbolInfo &symbols) {
|
||||
for (size_t i = 0; i < symbols.source_file_info.size(); ++i) {
|
||||
if (symbols.source_file_info[i].source_id != -1) {
|
||||
const char *name = symbols.source_file_info[i].name;
|
||||
if (!WriteFormat(fd, "FILE %d %s\n",
|
||||
symbols.source_file_info[i].source_id, name))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteOneFunction(int fd, int source_id,
|
||||
const struct FuncInfo &func_info){
|
||||
// Discard the ending part of the name.
|
||||
std::string func_name(func_info.name);
|
||||
std::string::size_type last_colon = func_name.find_last_of(':');
|
||||
if (last_colon != std::string::npos)
|
||||
func_name = func_name.substr(0, last_colon);
|
||||
func_name = Demangle(func_name.c_str());
|
||||
|
||||
if (func_info.size <= 0)
|
||||
return true;
|
||||
|
||||
if (WriteFormat(fd, "FUNC %lx %lx %d %s\n",
|
||||
func_info.rva_to_base,
|
||||
func_info.size,
|
||||
func_info.stack_param_size,
|
||||
func_name.c_str())) {
|
||||
for (size_t i = 0; i < func_info.line_info.size(); ++i) {
|
||||
const struct LineInfo &line_info = func_info.line_info[i];
|
||||
if (!WriteFormat(fd, "%lx %lx %d %d\n",
|
||||
line_info.rva_to_base,
|
||||
line_info.size,
|
||||
line_info.line_num,
|
||||
source_id))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WriteFunctionInfo(int fd, const struct SymbolInfo &symbols) {
|
||||
for (size_t i = 0; i < symbols.source_file_info.size(); ++i) {
|
||||
const struct SourceFileInfo &file_info = symbols.source_file_info[i];
|
||||
for (size_t j = 0; j < file_info.func_info.size(); ++j) {
|
||||
const struct FuncInfo &func_info = file_info.func_info[j];
|
||||
if (!WriteOneFunction(fd, file_info.source_id, func_info))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabSymbols(int fd, const struct SymbolInfo &symbols) {
|
||||
return WriteSourceFileInfo(fd, symbols) &&
|
||||
WriteFunctionInfo(fd, symbols);
|
||||
}
|
||||
|
||||
//
|
||||
// FDWrapper
|
||||
//
|
||||
// Wrapper class to make sure opened file is closed.
|
||||
//
|
||||
class FDWrapper {
|
||||
public:
|
||||
explicit FDWrapper(int fd) :
|
||||
fd_(fd) {
|
||||
}
|
||||
~FDWrapper() {
|
||||
if (fd_ != -1)
|
||||
close(fd_);
|
||||
}
|
||||
int get() {
|
||||
return fd_;
|
||||
}
|
||||
int release() {
|
||||
int fd = fd_;
|
||||
fd_ = -1;
|
||||
return fd;
|
||||
}
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
//
|
||||
// MmapWrapper
|
||||
//
|
||||
// Wrapper class to make sure mapped regions are unmapped.
|
||||
//
|
||||
class MmapWrapper {
|
||||
public:
|
||||
MmapWrapper(void *mapped_address, size_t mapped_size) :
|
||||
base_(mapped_address), size_(mapped_size) {
|
||||
}
|
||||
~MmapWrapper() {
|
||||
if (base_ != NULL) {
|
||||
assert(size_ > 0);
|
||||
munmap(base_, size_);
|
||||
}
|
||||
}
|
||||
void release() {
|
||||
base_ = NULL;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void *base_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &symbol_file) {
|
||||
int obj_fd = open(obj_file.c_str(), O_RDONLY);
|
||||
if (obj_fd < 0)
|
||||
return false;
|
||||
FDWrapper obj_fd_wrapper(obj_fd);
|
||||
struct stat st;
|
||||
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0)
|
||||
return false;
|
||||
void *obj_base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
|
||||
if (!obj_base)
|
||||
return false;
|
||||
MmapWrapper map_wrapper(obj_base, st.st_size);
|
||||
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base);
|
||||
if (!IsValidElf(elf_header))
|
||||
return false;
|
||||
struct SymbolInfo symbols;
|
||||
if (!LoadSymbols(elf_header, &symbols))
|
||||
return false;
|
||||
// Write to symbol file.
|
||||
int sym_fd = open(symbol_file.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
|
||||
if (sym_fd < 0)
|
||||
return false;
|
||||
FDWrapper sym_fd_wrapper(sym_fd);
|
||||
if (WriteModuleInfo(sym_fd, elf_header->e_machine, obj_file) &&
|
||||
DumpStabSymbols(sym_fd, symbols))
|
||||
return true;
|
||||
|
||||
// Remove the symbol file if failed to write the symbols.
|
||||
unlink(symbol_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
48
src/common/linux/dump_symbols.h
Normal file
48
src/common/linux/dump_symbols.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// dump_symbols.cc: Implements a linux stab debugging format dumper.
|
||||
//
|
||||
|
||||
#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
#define COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class DumpSymbols {
|
||||
public:
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &symbol_file);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DUMP_SYMBOLS_H__
|
143
src/common/linux/file_id.cc
Normal file
143
src/common/linux/file_id.cc
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// file_id.cc: Return a unique identifier for a file
|
||||
//
|
||||
// See file_id.h for documentation
|
||||
//
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <link.h>
|
||||
#include <sys/mman.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/linux/file_id.h"
|
||||
|
||||
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) {
|
||||
strncpy(path_, path, sizeof(path_));
|
||||
}
|
||||
|
||||
bool FileID::ElfFileIdentifier(unsigned char identifier[16]) {
|
||||
int fd = open(path_, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0 && st.st_size <= 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
void *base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
if (!base) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
const void *text_section = NULL;
|
||||
int text_size = 0;
|
||||
if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) {
|
||||
MD5_CTX md5;
|
||||
MD5_Init(&md5);
|
||||
MD5_Update(&md5, text_section, text_size);
|
||||
MD5_Final(identifier, &md5);
|
||||
success = true;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
munmap(base, st.st_size);
|
||||
return success;
|
||||
}
|
||||
|
||||
// static
|
||||
void FileID::ConvertIdentifierToString(const unsigned char identifier[16],
|
||||
char *buffer, int buffer_length) {
|
||||
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;
|
||||
|
||||
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
|
||||
buffer[buffer_idx++] = '-';
|
||||
|
||||
buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
|
||||
buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
|
||||
}
|
||||
|
||||
// NULL terminate
|
||||
buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
66
src/common/linux/file_id.h
Normal file
66
src/common/linux/file_id.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// file_id.h: Return a unique identifier for a file
|
||||
//
|
||||
|
||||
#ifndef COMMON_LINUX_FILE_ID_H__
|
||||
#define COMMON_LINUX_FILE_ID_H__
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class FileID {
|
||||
public:
|
||||
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]);
|
||||
|
||||
// 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);
|
||||
|
||||
private:
|
||||
// Storage for the path specified
|
||||
char path_[PATH_MAX];
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_FILE_ID_H__
|
||||
|
82
src/common/linux/guid_creator.cc
Normal file
82
src/common/linux/guid_creator.cc
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
//
|
||||
// GUIDGenerator
|
||||
//
|
||||
// This class is used to generate random GUID.
|
||||
// Currently use random number to generate a GUID since Linux has
|
||||
// no native GUID generator. This should be OK since we don't expect
|
||||
// crash to happen very offen.
|
||||
//
|
||||
class GUIDGenerator {
|
||||
public:
|
||||
GUIDGenerator() {
|
||||
srandom(time(NULL));
|
||||
}
|
||||
|
||||
bool CreateGUID(GUID *guid) const {
|
||||
guid->data1 = random();
|
||||
guid->data2 = (u_int16_t)(random());
|
||||
guid->data3 = (u_int16_t)(random());
|
||||
*reinterpret_cast<u_int32_t*>(&guid->data4[0]) = random();
|
||||
*reinterpret_cast<u_int32_t*>(&guid->data4[4]) = random();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Guid generator.
|
||||
const GUIDGenerator kGuidGenerator;
|
||||
|
||||
bool CreateGUID(GUID *guid) {
|
||||
return kGuidGenerator.CreateGUID(guid);
|
||||
};
|
||||
|
||||
// Parse guid to string.
|
||||
bool GUIDToString(const GUID *guid, char *buf, int buf_len) {
|
||||
// Should allow more space the the max length of GUID.
|
||||
assert(buf_len > kGUIDStringLength);
|
||||
int num = snprintf(buf, buf_len, kGUIDFormatString,
|
||||
guid->data1, guid->data2, guid->data3,
|
||||
*reinterpret_cast<const u_int32_t *>(&(guid->data4[0])),
|
||||
*reinterpret_cast<const u_int32_t *>(&(guid->data4[4])));
|
||||
if (num != kGUIDStringLength)
|
||||
return false;
|
||||
|
||||
buf[num] = '\0';
|
||||
return true;
|
||||
}
|
48
src/common/linux/guid_creator.h
Normal file
48
src/common/linux/guid_creator.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef COMMON_LINUX_GUID_CREATOR_H__
|
||||
#define COMMON_LINUX_GUID_CREATOR_H__
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
typedef MDGUID GUID;
|
||||
|
||||
// Format string for parsing GUID.
|
||||
#define kGUIDFormatString "%08x-%04x-%04x-%08x-%08x"
|
||||
// Length of GUID string. Don't count the ending '\0'.
|
||||
#define kGUIDStringLength 36
|
||||
|
||||
// Create a guid.
|
||||
bool CreateGUID(GUID *guid);
|
||||
|
||||
// Get the string from guid.
|
||||
bool GUIDToString(const GUID *guid, char *buf, int buf_len);
|
||||
|
||||
#endif
|
146
src/common/linux/http_upload.cc
Normal file
146
src/common/linux/http_upload.cc
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <cassert>
|
||||
#include <curl/curl.h>
|
||||
#include <curl/easy.h>
|
||||
#include <curl/types.h>
|
||||
|
||||
#include "common/linux/http_upload.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Callback to get the response data from server.
|
||||
static size_t WriteCallback(void *ptr, size_t size,
|
||||
size_t nmemb, void *userp) {
|
||||
if (!userp)
|
||||
return 0;
|
||||
|
||||
std::string *response = reinterpret_cast<std::string *>(userp);
|
||||
size_t real_size = size * nmemb;
|
||||
response->append(reinterpret_cast<char *>(ptr), real_size);
|
||||
return real_size;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static const char kUserAgent[] = "Breakpad/1.0 (Linux)";
|
||||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const string &url,
|
||||
const map<string, string> ¶meters,
|
||||
const string &upload_file,
|
||||
const string &file_part_name,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
string *response_body) {
|
||||
if (!CheckParameters(parameters))
|
||||
return false;
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
CURLcode err_code = CURLE_OK;
|
||||
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
|
||||
// Set proxy information if necessary.
|
||||
if (!proxy.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
|
||||
if (!proxy_user_pwd.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
|
||||
|
||||
struct curl_httppost *formpost = NULL;
|
||||
struct curl_httppost *lastptr = NULL;
|
||||
// Add form data.
|
||||
map<string, string>::const_iterator iter = parameters.begin();
|
||||
for (; iter != parameters.end(); ++iter)
|
||||
curl_formadd(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
CURLFORM_COPYCONTENTS, iter->second.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
// Add form file.
|
||||
curl_formadd(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, file_part_name.c_str(),
|
||||
CURLFORM_FILE, upload_file.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
|
||||
|
||||
// Disable 100-continue header.
|
||||
struct curl_slist *headerlist = NULL;
|
||||
char buf[] = "Expect:";
|
||||
headerlist = curl_slist_append(headerlist, buf);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||
|
||||
if (response_body != NULL) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA,
|
||||
reinterpret_cast<void *>(response_body));
|
||||
}
|
||||
|
||||
err_code = curl_easy_perform(curl);
|
||||
#ifndef NDEBUG
|
||||
if (err_code != CURLE_OK)
|
||||
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
|
||||
url.c_str(),
|
||||
curl_easy_strerror(err_code));
|
||||
#endif
|
||||
|
||||
if (curl != NULL)
|
||||
curl_easy_cleanup(curl);
|
||||
if (formpost != NULL)
|
||||
curl_formfree(formpost);
|
||||
if (headerlist != NULL)
|
||||
curl_slist_free_all(headerlist);
|
||||
return err_code == CURLE_OK;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::CheckParameters(const map<string, string> ¶meters) {
|
||||
for (map<string, string>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
const string &str = pos->first;
|
||||
if (str.size() == 0)
|
||||
return false; // disallow empty parameter names
|
||||
for (unsigned int i = 0; i < str.size(); ++i) {
|
||||
int c = str[i];
|
||||
if (c < 32 || c == '"' || c > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
80
src/common/linux/http_upload.h
Normal file
80
src/common/linux/http_upload.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
|
||||
// request using libcurl. It currently supports requests that contain
|
||||
// a set of string parameters (key/value pairs), and a file to upload.
|
||||
|
||||
#ifndef COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
#define COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
// Sends the given set of parameters, along with the contents of
|
||||
// upload_file, as a multipart POST request to the given URL.
|
||||
// file_part_name contains the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
static bool SendRequest(const string &url,
|
||||
const map<string, string> ¶meters,
|
||||
const string &upload_file,
|
||||
const string &file_part_name,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
string *response_body);
|
||||
|
||||
private:
|
||||
// Checks that the given list of parameters has only printable
|
||||
// ASCII characters in the parameter name, and does not contain
|
||||
// any quote (") characters. Returns true if so.
|
||||
static bool CheckParameters(const map<string, string> ¶meters);
|
||||
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
HTTPUpload();
|
||||
explicit HTTPUpload(const HTTPUpload &);
|
||||
void operator=(const HTTPUpload &);
|
||||
~HTTPUpload();
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_HTTP_UPLOAD_H__
|
28
src/tools/linux/dump_syms/Makefile
Normal file
28
src/tools/linux/dump_syms/Makefile
Normal file
|
@ -0,0 +1,28 @@
|
|||
CPP=g++
|
||||
CC=gcc
|
||||
|
||||
CPPFLAGS=-gstabs -I../../.. -Wall -D_REENTRANT
|
||||
LDFLAGS=-lssl
|
||||
|
||||
.PHONY:all clean
|
||||
|
||||
BIN=dump_syms
|
||||
|
||||
all:$(BIN)
|
||||
|
||||
DUMP_OBJ=dump_symbols.o guid_creator.o dump_syms.o file_id.o
|
||||
|
||||
dump_syms:$(DUMP_OBJ)
|
||||
$(CPP) $(CPPFLAGS) $(LDFLAGS) -o $@ $^
|
||||
|
||||
dump_symbols.o:../../../common/linux/dump_symbols.cc
|
||||
$(CPP) $(CPPFLAGS) -c $^
|
||||
|
||||
guid_creator.o:../../../common/linux/guid_creator.cc
|
||||
$(CPP) $(CPPFLAGS) -c $^
|
||||
|
||||
file_id.o:../../../common/linux/file_id.cc
|
||||
$(CPP) $(CPPFLAGS) -c $^
|
||||
|
||||
clean:
|
||||
rm -f $(BIN) $(DUMP_OBJ)
|
56
src/tools/linux/dump_syms/dump_syms.cc
Normal file
56
src/tools/linux/dump_syms/dump_syms.cc
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/linux/dump_symbols.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2 || argc > 3) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <binary-with-stab-symbol> [output-symbol-file]\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *binary = argv[1];
|
||||
std::string symbol_file(binary);
|
||||
symbol_file += ".sym";
|
||||
if (argc == 3)
|
||||
symbol_file = argv[2];
|
||||
|
||||
DumpSymbols dumper;
|
||||
if (dumper.WriteSymbolFile(binary, symbol_file))
|
||||
printf("Symbol file successfully written: %s\n", symbol_file.c_str());
|
||||
else
|
||||
printf("Failed to write symbol file.\n");
|
||||
return 0;
|
||||
}
|
24
src/tools/linux/symupload/Makefile
Normal file
24
src/tools/linux/symupload/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
CC=g++
|
||||
|
||||
CPPFLAGS=-gstabs -I../../.. -Wall -D_REENTRANT
|
||||
|
||||
.PHONY:all clean
|
||||
|
||||
BIN=minidump_upload sym_upload
|
||||
|
||||
all:$(BIN)
|
||||
|
||||
DUMP_UPLOAD_OBJ=minidump_upload.o http_upload.o
|
||||
SYM_UPLOAD_OBJ=sym_upload.o http_upload.o
|
||||
|
||||
minidump_upload:$(DUMP_UPLOAD_OBJ)
|
||||
$(CC) $(CPPFLAGS) `curl-config --libs` -o $@ $^
|
||||
|
||||
sym_upload:$(SYM_UPLOAD_OBJ)
|
||||
$(CC) $(CPPFLAGS) `curl-config --libs` -o $@ $^
|
||||
|
||||
http_upload.o:../../../common/linux/http_upload.cc
|
||||
$(CC) $(CPPFLAGS) `curl-config --cflags` -c $^
|
||||
|
||||
clean:
|
||||
rm *.o $(BIN)
|
143
src/tools/linux/symupload/minidump_upload.cc
Normal file
143
src/tools/linux/symupload/minidump_upload.cc
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// minidump_upload.m: Upload a minidump to a HTTP server. The upload is sent as
|
||||
// a multipart/form-data POST request with the following parameters:
|
||||
// prod: the product name
|
||||
// ver: the product version
|
||||
// symbol_file: the breakpad format symbol file
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/linux/http_upload.h"
|
||||
|
||||
using google_breakpad::HTTPUpload;
|
||||
|
||||
struct Options {
|
||||
std::string minidumpPath;
|
||||
std::string uploadURLStr;
|
||||
std::string product;
|
||||
std::string version;
|
||||
std::string proxy;
|
||||
std::string proxy_user_pwd;
|
||||
bool success;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
static void Start(Options *options) {
|
||||
std::map<std::string, std::string> parameters;
|
||||
// Add parameters
|
||||
parameters["prod"] = options->product;
|
||||
parameters["ver"] = options->version;
|
||||
|
||||
// Send it
|
||||
std::string response;
|
||||
bool success = HTTPUpload::SendRequest(options->uploadURLStr,
|
||||
parameters,
|
||||
options->minidumpPath,
|
||||
"upload_file_minidump",
|
||||
options->proxy,
|
||||
options->proxy_user_pwd,
|
||||
&response);
|
||||
|
||||
if (success) {
|
||||
printf("Successfully sent the minidump file.\n");
|
||||
} else {
|
||||
printf("Failed to send minidump\n");
|
||||
printf("Response:\n");
|
||||
printf("%s\n", response.c_str());
|
||||
}
|
||||
options->success = success;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static void
|
||||
Usage(int argc, const char *argv[]) {
|
||||
fprintf(stderr, "Submit minidump information.\n");
|
||||
fprintf(stderr, "Usage: %s [options...] -p <product> -v <version> <minidump> "
|
||||
"<upload-URL>\n", argv[0]);
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr, "<minidump> should be a minidump.\n");
|
||||
fprintf(stderr, "<upload-URL> is the destination for the upload\n");
|
||||
|
||||
fprintf(stderr, "-p:\t <product> Product name\n");
|
||||
fprintf(stderr, "-v:\t <version> Product version\n");
|
||||
fprintf(stderr, "-x:\t <host[:port]> Use HTTP proxy on given port\n");
|
||||
fprintf(stderr, "-u:\t <user[:password]> Set proxy user and password\n");
|
||||
fprintf(stderr, "-h:\t Usage\n");
|
||||
fprintf(stderr, "-?:\t Usage\n");
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static void
|
||||
SetupOptions(int argc, const char *argv[], Options *options) {
|
||||
extern int optind;
|
||||
char ch;
|
||||
|
||||
while ((ch = getopt(argc, (char * const *)argv, "p:u:v:x:h?")) != -1) {
|
||||
switch (ch) {
|
||||
case 'p':
|
||||
options->product = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
options->proxy_user_pwd = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
options->version = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
options->proxy = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
Usage(argc, argv);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 2) {
|
||||
fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]);
|
||||
Usage(argc, argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
options->minidumpPath = argv[optind];
|
||||
options->uploadURLStr = argv[optind + 1];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int main (int argc, const char * argv[]) {
|
||||
Options options;
|
||||
SetupOptions(argc, argv, &options);
|
||||
Start(&options);
|
||||
return options.success ? 0 : 1;
|
||||
}
|
219
src/tools/linux/symupload/sym_upload.cc
Normal file
219
src/tools/linux/symupload/sym_upload.cc
Normal file
|
@ -0,0 +1,219 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// symupload.cc: Upload a symbol file to a HTTP server. The upload is sent as
|
||||
// a multipart/form-data POST request with the following parameters:
|
||||
// code_file: the basename of the module, e.g. "app"
|
||||
// debug_file: the basename of the debugging file, e.g. "app"
|
||||
// debug_identifier: the debug file's identifier, usually consisting of
|
||||
// the guid and age embedded in the pdb, e.g.
|
||||
// "11111111BBBB3333DDDD555555555555F"
|
||||
// version: the file version of the module, e.g. "1.2.3.4"
|
||||
// os: the operating system that the module was built for
|
||||
// cpu: the CPU that the module was built for
|
||||
// symbol_file: the contents of the breakpad-format symbol file
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/http_upload.h"
|
||||
|
||||
using google_breakpad::HTTPUpload;
|
||||
|
||||
typedef struct {
|
||||
std::string symbolsPath;
|
||||
std::string uploadURLStr;
|
||||
std::string proxy;
|
||||
std::string proxy_user_pwd;
|
||||
std::string version;
|
||||
bool success;
|
||||
} Options;
|
||||
|
||||
static void TokenizeByChar(const std::string &source_string,
|
||||
int c, std::vector<std::string> *results) {
|
||||
assert(results);
|
||||
std::string::size_type cur_pos = 0, next_pos = 0;
|
||||
while ((next_pos = source_string.find(c, cur_pos)) != std::string::npos) {
|
||||
if (next_pos != cur_pos)
|
||||
results->push_back(source_string.substr(cur_pos, next_pos - cur_pos));
|
||||
cur_pos = next_pos + 1;
|
||||
}
|
||||
if (cur_pos < source_string.size() && next_pos != cur_pos)
|
||||
results->push_back(source_string.substr(cur_pos));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Parse out the module line which have 6 parts.
|
||||
// MODULE <os> <cpu> <uuid> <age> <module-name>
|
||||
static bool ModuleDataForSymbolFile(const std::string &file,
|
||||
std::vector<std::string> *module_parts) {
|
||||
assert(module_parts);
|
||||
const size_t kModulePartNumber = 6;
|
||||
FILE *fp = fopen(file.c_str(), "r");
|
||||
if (fp) {
|
||||
char buffer[1024];
|
||||
if (fgets(buffer, sizeof(buffer), fp)) {
|
||||
std::string line(buffer);
|
||||
std::string::size_type line_break_pos = line.find_first_of('\n');
|
||||
if (line_break_pos == std::string::npos) {
|
||||
assert(!"The file is invalid!");
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
line.resize(line_break_pos);
|
||||
const char kDelimiter = ' ';
|
||||
TokenizeByChar(line, kDelimiter, module_parts);
|
||||
if (module_parts->size() != kModulePartNumber)
|
||||
module_parts->clear();
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return module_parts->size() == kModulePartNumber;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static std::string CompactIdentifier(const std::string &uuid,
|
||||
const std::string &age) {
|
||||
std::vector<std::string> components;
|
||||
TokenizeByChar(uuid, '-', &components);
|
||||
std::string result;
|
||||
for (size_t i = 0; i < components.size(); ++i)
|
||||
result += components[i];
|
||||
result += age;
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static void Start(Options *options) {
|
||||
std::map<std::string, std::string> parameters;
|
||||
options->success = false;
|
||||
std::vector<std::string> module_parts;
|
||||
if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) {
|
||||
fprintf(stderr, "Failed to parse symbol file!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string compacted_id = CompactIdentifier(module_parts[3],
|
||||
module_parts[4]);
|
||||
|
||||
// Add parameters
|
||||
if (!options->version.empty())
|
||||
parameters["version"] = options->version;
|
||||
|
||||
// MODULE <os> <cpu> <uuid> <age> <module-name>
|
||||
// 0 1 2 3 4 5
|
||||
parameters["age"] = "1";
|
||||
parameters["os"] = module_parts[1];
|
||||
parameters["cpu"] = module_parts[2];
|
||||
parameters["debug_file"] = module_parts[5];
|
||||
parameters["code_file"] = module_parts[5];
|
||||
parameters["debug_identifier"] = compacted_id;
|
||||
std::string response;
|
||||
bool success = HTTPUpload::SendRequest(options->uploadURLStr,
|
||||
parameters,
|
||||
options->symbolsPath,
|
||||
"symbol_file",
|
||||
options->proxy,
|
||||
options->proxy_user_pwd,
|
||||
&response);
|
||||
|
||||
if (success) {
|
||||
printf("Successfully sent the symbol file.\n");
|
||||
} else {
|
||||
printf("Failed to send symbol file.\n");
|
||||
printf("Response:\n");
|
||||
printf("%s\n", response.c_str());
|
||||
}
|
||||
options->success = success;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static void
|
||||
Usage(int argc, const char *argv[]) {
|
||||
fprintf(stderr, "Submit symbol information.\n");
|
||||
fprintf(stderr, "Usage: %s [options...] <symbols> <upload-URL>\n", argv[0]);
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr, "<symbols> should be created by using the dump_syms tool.\n");
|
||||
fprintf(stderr, "<upload-URL> is the destination for the upload\n");
|
||||
fprintf(stderr, "-v:\t Version information (e.g., 1.2.3.4)\n");
|
||||
fprintf(stderr, "-x:\t <host[:port]> Use HTTP proxy on given port\n");
|
||||
fprintf(stderr, "-u:\t <user[:password]> Set proxy user and password\n");
|
||||
fprintf(stderr, "-h:\t Usage\n");
|
||||
fprintf(stderr, "-?:\t Usage\n");
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static void
|
||||
SetupOptions(int argc, const char *argv[], Options *options) {
|
||||
extern int optind;
|
||||
char ch;
|
||||
|
||||
while ((ch = getopt(argc, (char * const *)argv, "u:v:x:h?")) != -1) {
|
||||
switch (ch) {
|
||||
case 'u':
|
||||
options->proxy_user_pwd = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
options->version = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
options->proxy = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
Usage(argc, argv);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 2) {
|
||||
fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]);
|
||||
Usage(argc, argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
options->symbolsPath = argv[optind];
|
||||
options->uploadURLStr = argv[optind + 1];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int main (int argc, const char * argv[]) {
|
||||
Options options;
|
||||
SetupOptions(argc, argv, &options);
|
||||
Start(&options);
|
||||
return options.success ? 0 : 1;
|
||||
}
|
Loading…
Reference in a new issue