From 43c933d7f8e490d9dbf3f939f3b2f095a170dc84 Mon Sep 17 00:00:00 2001 From: "jcivelli@chromium.org" Date: Thu, 9 Aug 2012 22:59:58 +0000 Subject: Adding a way to create an ExceptionHandler that takes in a file descriptor where the minidump should be created, without the need of opening any other file. BUG=None TEST=Run unit-tests. git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1007 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/linux/handler/exception_handler.cc | 116 ++--- src/client/linux/handler/exception_handler.h | 92 ++-- .../linux/handler/exception_handler_unittest.cc | 478 +++++++++++---------- src/client/linux/handler/minidump_descriptor.cc | 61 +++ src/client/linux/handler/minidump_descriptor.h | 84 ++++ 5 files changed, 474 insertions(+), 357 deletions(-) create mode 100644 src/client/linux/handler/minidump_descriptor.cc create mode 100644 src/client/linux/handler/minidump_descriptor.h (limited to 'src/client/linux/handler') diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index f505ff33..73c3bdfb 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -93,7 +93,6 @@ #include "client/linux/log/log.h" #include "client/linux/minidump_writer/linux_dumper.h" #include "client/linux/minidump_writer/minidump_writer.h" -#include "common/linux/guid_creator.h" #include "common/linux/eintr_wrapper.h" #include "third_party/lss/linux_syscall_support.h" @@ -126,59 +125,38 @@ pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER; // Runs before crashing: normal context. -ExceptionHandler::ExceptionHandler(const string &dump_path, - FilterCallback filter, - MinidumpCallback callback, - void *callback_context, - bool install_handler) - : filter_(filter), - callback_(callback), - callback_context_(callback_context), - handler_installed_(install_handler) -{ - Init(dump_path, -1); -} - -ExceptionHandler::ExceptionHandler(const string &dump_path, +ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, FilterCallback filter, MinidumpCallback callback, void* callback_context, bool install_handler, const int server_fd) - : filter_(filter), - callback_(callback), - callback_context_(callback_context), - handler_installed_(install_handler) -{ - Init(dump_path, server_fd); -} - -// Runs before crashing: normal context. -ExceptionHandler::~ExceptionHandler() { - UninstallHandlers(); -} - -void ExceptionHandler::Init(const string &dump_path, - const int server_fd) -{ - crash_handler_ = NULL; - if (0 <= server_fd) - crash_generation_client_ - .reset(CrashGenerationClient::TryCreate(server_fd)); - - if (handler_installed_) + : filter_(filter), + callback_(callback), + callback_context_(callback_context), + minidump_descriptor_(descriptor), + crash_handler_(NULL) { + if (server_fd >= 0) + crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); + + if (install_handler) InstallHandlers(); - if (!IsOutOfProcess()) - set_dump_path(dump_path); + if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) + minidump_descriptor_.UpdatePath(); pthread_mutex_lock(&handler_stack_mutex_); - if (handler_stack_ == NULL) - handler_stack_ = new std::vector; + if (!handler_stack_) + handler_stack_ = new std::vector; handler_stack_->push_back(this); pthread_mutex_unlock(&handler_stack_mutex_); } +// Runs before crashing: normal context. +ExceptionHandler::~ExceptionHandler() { + UninstallHandlers(); +} + // Runs before crashing: normal context. bool ExceptionHandler::InstallHandlers() { // We run the signal handlers on an alternative stack because we might have @@ -237,24 +215,6 @@ void ExceptionHandler::UninstallHandlers() { old_handlers_.clear(); } -// Runs before crashing: normal context. -void ExceptionHandler::UpdateNextID() { - GUID guid; - char guid_str[kGUIDStringLength + 1]; - if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { - next_minidump_id_ = guid_str; - next_minidump_id_c_ = next_minidump_id_.c_str(); - - char minidump_path[PATH_MAX]; - snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", - dump_path_c_, - guid_str); - - next_minidump_path_ = minidump_path; - next_minidump_path_c_ = next_minidump_path_.c_str(); - } -} - // void ExceptionHandler::set_crash_handler(HandlerCallback callback) { // crash_handler_ = callback; // } @@ -308,6 +268,7 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { struct ThreadArgument { pid_t pid; // the crashing process + const MinidumpDescriptor* minidump_descriptor; ExceptionHandler* handler; const void* context; // a CrashContext structure size_t context_size; @@ -378,6 +339,7 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) { ThreadArgument thread_arg; thread_arg.handler = this; + thread_arg.minidump_descriptor = &minidump_descriptor_; thread_arg.pid = getpid(); thread_arg.context = context; thread_arg.context_size = sizeof(*context); @@ -420,11 +382,8 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) { } bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; - if (callback_) - success = callback_(dump_path_c_, next_minidump_id_c_, - callback_context_, success); - + success = callback_(minidump_descriptor_, callback_context_, success); return success; } @@ -461,7 +420,14 @@ void ExceptionHandler::WaitForContinueSignal() { // Runs on the cloned process. bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, size_t context_size) { - return google_breakpad::WriteMinidump(next_minidump_path_c_, + if (minidump_descriptor_.IsFD()) { + return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), + crashing_process, + context, + context_size, + mapping_list_); + } + return google_breakpad::WriteMinidump(minidump_descriptor_.path(), crashing_process, context, context_size, @@ -469,15 +435,29 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, } // static -bool ExceptionHandler::WriteMinidump(const string &dump_path, +bool ExceptionHandler::WriteMinidump(const string& dump_path, MinidumpCallback callback, void* callback_context) { - ExceptionHandler eh(dump_path, NULL, callback, callback_context, false); + MinidumpDescriptor descriptor(dump_path); + ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); return eh.WriteMinidump(); } bool ExceptionHandler::WriteMinidump() { #if !defined(__ARM_EABI__) + if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) { + // Update the path of the minidump so that this can be called multiple times + // and new files are created for each minidump. This is done before the + // generation happens, as clients may want to access the MinidumpDescriptor + // after this call to find the exact path to the minidump file. + minidump_descriptor_.UpdatePath(); + } else if (minidump_descriptor_.IsFD()) { + // Reposition the FD to its beginning and resize it to get rid of the + // previous minidump info. + lseek(minidump_descriptor_.fd(), 0, SEEK_SET); + ftruncate(minidump_descriptor_.fd(), 0); + } + // Allow ourselves to be dumped. sys_prctl(PR_SET_DUMPABLE, 1); @@ -489,9 +469,7 @@ bool ExceptionHandler::WriteMinidump() { sizeof(context.float_state)); context.tid = sys_gettid(); - bool success = GenerateDump(&context); - UpdateNextID(); - return success; + return GenerateDump(&context); #else return false; #endif // !defined(__ARM_EABI__) diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 4bc8ba72..c339b7dd 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -42,6 +42,7 @@ #include "client/linux/android_ucontext.h" #endif #include "client/linux/crash_generation/crash_generation_client.h" +#include "client/linux/handler/minidump_descriptor.h" #include "client/linux/minidump_writer/minidump_writer.h" #include "common/using_std_string.h" #include "google_breakpad/common/minidump_format.h" @@ -51,8 +52,6 @@ struct sigaction; namespace google_breakpad { -class ExceptionHandler; - // ExceptionHandler // // ExceptionHandler can write a minidump file when an exception occurs, @@ -71,17 +70,18 @@ class ExceptionHandler; // 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. +// which receives the full path or file descriptor of the minidump. The +// caller can collect and write additional application state to that minidump, +// and 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 + // 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, @@ -91,10 +91,10 @@ class ExceptionHandler { 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 \.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. + // |descriptor| contains the file descriptor or file path containing the + // minidump. |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 @@ -106,9 +106,8 @@ class ExceptionHandler { // 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, + typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor, + void* context, bool succeeded); // In certain cases, a user may wish to handle the generation of the minidump @@ -121,52 +120,52 @@ class ExceptionHandler { void* context); // Creates a new ExceptionHandler instance to handle writing minidumps. - // Before writing a minidump, the optional filter callback will be called. + // 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. + // minidump. The minidump content will be written to the file path or file + // descriptor from |descriptor|, 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, + // If |server_fd| is valid, the minidump is generated out-of-process. If it + // is -1, in-process generation will always be used. + ExceptionHandler(const MinidumpDescriptor& descriptor, + FilterCallback filter, + MinidumpCallback callback, void *callback_context, - bool install_handler); - - // Creates a new ExceptionHandler instance that can attempt to - // perform out-of-process dump generation if server_fd is valid. If - // server_fd is invalid, in-process dump generation will be - // used. See the above ctor for a description of the other - // parameters. - ExceptionHandler(const string& dump_path, - FilterCallback filter, MinidumpCallback callback, - void* callback_context, bool install_handler, const int server_fd); - ~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(); - UpdateNextID(); + const MinidumpDescriptor& minidump_descriptor() const { + return minidump_descriptor_; } void set_crash_handler(HandlerCallback callback) { crash_handler_ = callback; } - // Writes a minidump immediately. This can be used to capture the - // execution state independently of a crash. Returns true on success. + // Writes a minidump immediately. This can be used to capture the execution + // state independently of a crash. + // Returns true on success. + // If the ExceptionHandler has been created with a path, a new file is + // generated for each minidump. The file path can be retrieved in the + // MinidumpDescriptor passed to the MinidumpCallback or by accessing the + // MinidumpDescriptor directly from the ExceptionHandler (with + // minidump_descriptor()). + // If the ExceptionHandler has been created with a file descriptor, the file + // descriptor is repositioned to its beginning and the previous generated + // minidump is overwritten. + // Note that this method is not supposed to be called from a compromised + // context as it uses the heap. bool WriteMinidump(); // Convenience form of WriteMinidump which does not require an // ExceptionHandler instance. - static bool WriteMinidump(const string &dump_path, + static bool WriteMinidump(const string& dump_path, MinidumpCallback callback, - void *callback_context); + void* callback_context); // This structure is passed to minidump_writer.h:WriteMinidump via an opaque // blob. It shouldn't be needed in any user code. @@ -195,8 +194,6 @@ class ExceptionHandler { size_t file_offset); private: - void Init(const string &dump_path, - const int server_fd); bool InstallHandlers(); void UninstallHandlers(); void PreresolveSymbols(); @@ -204,7 +201,6 @@ class ExceptionHandler { void SendContinueSignalToChild(); void WaitForContinueSignal(); - void UpdateNextID(); static void SignalHandler(int sig, siginfo_t* info, void* uc); bool HandleSignal(int sig, siginfo_t* info, void* uc); static int ThreadEntry(void* arg); @@ -217,18 +213,8 @@ class ExceptionHandler { scoped_ptr crash_generation_client_; - string dump_path_; - string next_minidump_path_; - string next_minidump_id_; - - // Pointers to C-string representations of the above. These are set - // when the above are set so we can avoid calling c_str during - // an exception. - const char* dump_path_c_; - const char* next_minidump_path_c_; - const char* next_minidump_id_c_; + MinidumpDescriptor minidump_descriptor_; - const bool handler_installed_; HandlerCallback crash_handler_; // The global exception handler stack. This is need becuase there may exist diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc index ea2652d5..6ad3661e 100644 --- a/src/client/linux/handler/exception_handler_unittest.cc +++ b/src/client/linux/handler/exception_handler_unittest.cc @@ -52,11 +52,25 @@ using namespace google_breakpad; +namespace { + // Length of a formatted GUID string = // sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator) const int kGUIDStringSize = 37; -static void sigchld_handler(int signo) { } +void sigchld_handler(int signo) { } + +int CreateTMPFile(const std::string& dir, std::string* path) { + std::string file = dir + "/exception-handler-unittest.XXXXXX"; + const char* c_file = file.c_str(); + // Copy that string, mkstemp needs a C string it can modify. + char* c_path = strdup(c_file); + const int fd = mkstemp(c_path); + if (fd >= 0) + *path = c_path; + free(c_path); + return fd; +} class ExceptionHandlerTest : public ::testing::Test { protected: @@ -75,70 +89,117 @@ class ExceptionHandlerTest : public ::testing::Test { struct sigaction old_action; }; -TEST(ExceptionHandlerTest, Simple) { + +void WaitForProcessToTerminate(pid_t process_id, int expected_status) { + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(process_id, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(expected_status, WTERMSIG(status)); +} + +// Reads the minidump path sent over the pipe |fd| and sets it in |path|. +void ReadMinidumpPathFromPipe(int fd, std::string* path) { + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(1, r); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(static_cast(sizeof(len)), read(fd, &len, sizeof(len))); + ASSERT_LT(len, static_cast(2048)); + char* filename = static_cast(malloc(len + 1)); + ASSERT_EQ(len, read(fd, filename, len)); + filename[len] = 0; + close(fd); + *path = filename; + free(filename); +} + +} // namespace + +TEST(ExceptionHandlerTest, SimpleWithPath) { + AutoTempDir temp_dir; + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); +} + +TEST(ExceptionHandlerTest, SimpleWithFD) { AutoTempDir temp_dir; - ExceptionHandler handler(temp_dir.path(), NULL, NULL, NULL, true); + std::string path; + const int fd = CreateTMPFile(temp_dir.path(), &path); + ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, true, -1); + close(fd); } -static bool DoneCallback(const char* dump_path, - const char* minidump_id, +static bool DoneCallback(const MinidumpDescriptor& descriptor, void* context, bool succeeded) { if (!succeeded) - return succeeded; - - int fd = (intptr_t) context; - uint32_t len = my_strlen(minidump_id); - IGNORE_RET(HANDLE_EINTR(sys_write(fd, &len, sizeof(len)))); - IGNORE_RET(HANDLE_EINTR(sys_write(fd, minidump_id, len))); - sys_close(fd); + return false; + if (!descriptor.IsFD()) { + int fd = reinterpret_cast(context); + uint32_t len = 0; + len = my_strlen(descriptor.path()); + IGNORE_RET(HANDLE_EINTR(sys_write(fd, &len, sizeof(len)))); + IGNORE_RET(HANDLE_EINTR(sys_write(fd, descriptor.path(), len))); + } return true; } -TEST(ExceptionHandlerTest, ChildCrash) { +void ChildCrash(bool use_fd) { AutoTempDir temp_dir; - int fds[2]; - ASSERT_NE(pipe(fds), -1); + int fds[2] = {0}; + int minidump_fd = -1; + std::string minidump_path; + if (use_fd) { + minidump_fd = CreateTMPFile(temp_dir.path(), &minidump_path); + } else { + ASSERT_NE(pipe(fds), -1); + } const pid_t child = fork(); if (child == 0) { - close(fds[0]); - ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, (void*) fds[1], - true); - *reinterpret_cast(NULL) = 0; + { + scoped_ptr handler; + if (use_fd) { + handler.reset(new ExceptionHandler(MinidumpDescriptor(minidump_fd), + NULL, NULL, NULL, true, -1)); + } else { + close(fds[0]); // Close the reading end. + void* fd_param = reinterpret_cast(fds[1]); + handler.reset(new ExceptionHandler(MinidumpDescriptor(temp_dir.path()), + NULL, DoneCallback, fd_param, + true, -1)); + } + // Crash with the exception handler in scope. + *reinterpret_cast(NULL) = 0; + } } - close(fds[1]); + if (!use_fd) + close(fds[1]); // Close the writting end. - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGSEGV); + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); - ASSERT_EQ(r, 1); - ASSERT_TRUE(pfd.revents & POLLIN); - - uint32_t len; - ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); - ASSERT_LT(len, (uint32_t)2048); - char* filename = reinterpret_cast(malloc(len + 1)); - ASSERT_EQ(read(fds[0], filename, len), len); - filename[len] = 0; - close(fds[0]); - - const string minidump_filename = temp_dir.path() + "/" + filename + - ".dmp"; + if (!use_fd) + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); struct stat st; - ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); ASSERT_GT(st.st_size, 0u); - unlink(minidump_filename.c_str()); + unlink(minidump_path.c_str()); +} + +TEST(ExceptionHandlerTest, ChildCrashWithPath) { + ASSERT_NO_FATAL_FAILURE(ChildCrash(false)); +} + +TEST(ExceptionHandlerTest, ChildCrashWithFD) { + ASSERT_NO_FATAL_FAILURE(ChildCrash(true)); } // Test that memory around the instruction pointer is written @@ -158,8 +219,9 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, - (void*) fds[1], true); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); // Get some executable memory. char* memory = reinterpret_cast(mmap(NULL, @@ -179,45 +241,25 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { // Now execute the instructions, which should crash. typedef void (*void_function)(void); void_function memory_function = - reinterpret_cast(memory + kOffset); + reinterpret_cast(memory + kOffset); memory_function(); } close(fds[1]); - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGILL); + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); - ASSERT_EQ(r, 1); - ASSERT_TRUE(pfd.revents & POLLIN); - - uint32_t len; - ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); - ASSERT_LT(len, (uint32_t)2048); - char* filename = reinterpret_cast(malloc(len + 1)); - ASSERT_EQ(read(fds[0], filename, len), len); - filename[len] = 0; - close(fds[0]); - - const string minidump_filename = temp_dir.path() + "/" + filename + - ".dmp"; + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); struct stat st; - ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); ASSERT_GT(st.st_size, 0u); // Read the minidump. Locate the exception record and the // memory list, and then ensure that there is a memory region // in the memory list that covers the instruction pointer from // the exception record. - Minidump minidump(minidump_filename); + Minidump minidump(minidump_path); ASSERT_TRUE(minidump.Read()); MinidumpException* exception = minidump.GetException(); @@ -246,7 +288,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { } MinidumpMemoryRegion* region = - memory_list->GetMemoryRegionForAddress(instruction_pointer); + memory_list->GetMemoryRegionForAddress(instruction_pointer); ASSERT_TRUE(region); EXPECT_EQ(kMemorySize, region->GetSize()); @@ -262,8 +304,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), suffix_bytes, sizeof(suffix_bytes)) == 0); - unlink(minidump_filename.c_str()); - free(filename); + unlink(minidump_path.c_str()); } // Test that the memory region around the instruction pointer is @@ -283,16 +324,17 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, - (void*) fds[1], true); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); // Get some executable memory. char* memory = - reinterpret_cast(mmap(NULL, - kMemorySize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); if (!memory) exit(0); @@ -304,45 +346,25 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { // Now execute the instructions, which should crash. typedef void (*void_function)(void); void_function memory_function = - reinterpret_cast(memory + kOffset); + reinterpret_cast(memory + kOffset); memory_function(); } close(fds[1]); - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGILL); - - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); - ASSERT_EQ(r, 1); - ASSERT_TRUE(pfd.revents & POLLIN); - - uint32_t len; - ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); - ASSERT_LT(len, (uint32_t)2048); - char* filename = reinterpret_cast(malloc(len + 1)); - ASSERT_EQ(read(fds[0], filename, len), len); - filename[len] = 0; - close(fds[0]); + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); - const string minidump_filename = temp_dir.path() + "/" + filename + - ".dmp"; + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); struct stat st; - ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); ASSERT_GT(st.st_size, 0u); // Read the minidump. Locate the exception record and the // memory list, and then ensure that there is a memory region // in the memory list that covers the instruction pointer from // the exception record. - Minidump minidump(minidump_filename); + Minidump minidump(minidump_path); ASSERT_TRUE(minidump.Read()); MinidumpException* exception = minidump.GetException(); @@ -371,7 +393,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { } MinidumpMemoryRegion* region = - memory_list->GetMemoryRegionForAddress(instruction_pointer); + memory_list->GetMemoryRegionForAddress(instruction_pointer); ASSERT_TRUE(region); EXPECT_EQ(kMemorySize / 2, region->GetSize()); @@ -383,9 +405,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), suffix_bytes, sizeof(suffix_bytes)) == 0); - - unlink(minidump_filename.c_str()); - free(filename); + unlink(minidump_path.c_str()); } // Test that the memory region around the instruction pointer is @@ -408,16 +428,17 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, - (void*) fds[1], true); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); // Get some executable memory. char* memory = - reinterpret_cast(mmap(NULL, - kMemorySize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); if (!memory) exit(0); @@ -429,45 +450,24 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { // Now execute the instructions, which should crash. typedef void (*void_function)(void); void_function memory_function = - reinterpret_cast(memory + kOffset); + reinterpret_cast(memory + kOffset); memory_function(); } close(fds[1]); - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGILL); - - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); - ASSERT_EQ(r, 1); - ASSERT_TRUE(pfd.revents & POLLIN); - - uint32_t len; - ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); - ASSERT_LT(len, (uint32_t)2048); - char* filename = reinterpret_cast(malloc(len + 1)); - ASSERT_EQ(read(fds[0], filename, len), len); - filename[len] = 0; - close(fds[0]); + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); - const string minidump_filename = temp_dir.path() + "/" + filename + - ".dmp"; + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); struct stat st; - ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); ASSERT_GT(st.st_size, 0u); - // Read the minidump. Locate the exception record and the - // memory list, and then ensure that there is a memory region - // in the memory list that covers the instruction pointer from - // the exception record. - Minidump minidump(minidump_filename); + // Read the minidump. Locate the exception record and the memory list, and + // then ensure that there is a memory region in the memory list that covers + // the instruction pointer from the exception record. + Minidump minidump(minidump_path); ASSERT_TRUE(minidump.Read()); MinidumpException* exception = minidump.GetException(); @@ -496,7 +496,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { } MinidumpMemoryRegion* region = - memory_list->GetMemoryRegionForAddress(instruction_pointer); + memory_list->GetMemoryRegionForAddress(instruction_pointer); ASSERT_TRUE(region); const size_t kPrefixSize = 128; // bytes @@ -510,8 +510,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { EXPECT_TRUE(memcmp(bytes + kPrefixSize, instructions, sizeof(instructions)) == 0); - unlink(minidump_filename.c_str()); - free(filename); + unlink(minidump_path.c_str()); } // If AddressSanitizer is used, NULL pointer dereferences generate SIGILL @@ -520,89 +519,52 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { // this test if AddressSanitizer is used. #ifndef ADDRESS_SANITIZER -// Ensure that an extra memory block doesn't get added when the -// instruction pointer is not in mapped memory. +// Ensure that an extra memory block doesn't get added when the instruction +// pointer is not in mapped memory. TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { AutoTempDir temp_dir; int fds[2]; ASSERT_NE(pipe(fds), -1); - const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, - (void*) fds[1], true); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); // Try calling a NULL pointer. typedef void (*void_function)(void); - void_function memory_function = - reinterpret_cast(NULL); + void_function memory_function = reinterpret_cast(NULL); memory_function(); } close(fds[1]); - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGSEGV); - - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); - ASSERT_EQ(r, 1); - ASSERT_TRUE(pfd.revents & POLLIN); + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); - uint32_t len; - ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); - ASSERT_LT(len, (uint32_t)2048); - char* filename = reinterpret_cast(malloc(len + 1)); - ASSERT_EQ(read(fds[0], filename, len), len); - filename[len] = 0; - close(fds[0]); - - const string minidump_filename = temp_dir.path() + "/" + filename + - ".dmp"; + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); struct stat st; - ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); ASSERT_GT(st.st_size, 0u); // Read the minidump. Locate the exception record and the // memory list, and then ensure that there is a memory region // in the memory list that covers the instruction pointer from // the exception record. - Minidump minidump(minidump_filename); + Minidump minidump(minidump_path); ASSERT_TRUE(minidump.Read()); MinidumpException* exception = minidump.GetException(); MinidumpMemoryList* memory_list = minidump.GetMemoryList(); ASSERT_TRUE(exception); ASSERT_TRUE(memory_list); - ASSERT_EQ((unsigned int)1, memory_list->region_count()); + ASSERT_EQ(static_cast(1), memory_list->region_count()); - unlink(minidump_filename.c_str()); - free(filename); + unlink(minidump_path.c_str()); } #endif // !ADDRESS_SANITIZER -static bool SimpleCallback(const char* dump_path, - const char* minidump_id, - void* context, - bool succeeded) { - if (!succeeded) - return succeeded; - - string* minidump_file = reinterpret_cast(context); - minidump_file->append(dump_path); - minidump_file->append("/"); - minidump_file->append(minidump_id); - minidump_file->append(".dmp"); - return true; -} - // Test that anonymous memory maps can be annotated with names and IDs. TEST(ExceptionHandlerTest, ModuleInfo) { // These are defined here so the parent can use them to check the @@ -629,37 +591,37 @@ TEST(ExceptionHandlerTest, ModuleInfo) { // Get some memory. char* memory = - reinterpret_cast(mmap(NULL, - kMemorySize, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); const uintptr_t kMemoryAddress = reinterpret_cast(memory); ASSERT_TRUE(memory); - string minidump_filename; AutoTempDir temp_dir; - ExceptionHandler handler(temp_dir.path(), NULL, SimpleCallback, - (void*)&minidump_filename, true); + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + // Add info about the anonymous memory mapping. handler.AddMappingInfo(kMemoryName, kModuleGUID, kMemoryAddress, kMemorySize, 0); - handler.WriteMinidump(); + ASSERT_TRUE(handler.WriteMinidump()); - // Read the minidump. Load the module list, and ensure that - // the mmap'ed |memory| is listed with the given module name - // and debug ID. - Minidump minidump(minidump_filename); + const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); + // Read the minidump. Load the module list, and ensure that the mmap'ed + // |memory| is listed with the given module name and debug ID. + Minidump minidump(minidump_desc.path()); ASSERT_TRUE(minidump.Read()); MinidumpModuleList* module_list = minidump.GetModuleList(); ASSERT_TRUE(module_list); const MinidumpModule* module = - module_list->GetModuleForAddress(kMemoryAddress); + module_list->GetModuleForAddress(kMemoryAddress); ASSERT_TRUE(module); EXPECT_EQ(kMemoryAddress, module->base_address()); @@ -667,7 +629,7 @@ TEST(ExceptionHandlerTest, ModuleInfo) { EXPECT_EQ(kMemoryName, module->code_file()); EXPECT_EQ(module_identifier, module->debug_identifier()); - unlink(minidump_filename.c_str()); + unlink(minidump_desc.path()); } static const unsigned kControlMsgSize = @@ -732,7 +694,8 @@ TEST(ExceptionHandlerTest, ExternalDumper) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true); + ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL, + reinterpret_cast(fds[1]), true, -1); handler.set_crash_handler(CrashHandler); *reinterpret_cast(NULL) = 0; } @@ -751,10 +714,10 @@ TEST(ExceptionHandlerTest, ExternalDumper) { msg.msg_controllen = kControlMsgSize; const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); - ASSERT_EQ(n, kCrashContextSize); - ASSERT_EQ(msg.msg_controllen, kControlMsgSize); - ASSERT_EQ(msg.msg_flags, 0); - ASSERT_EQ(close(fds[0]), 0); + ASSERT_EQ(kCrashContextSize, n); + ASSERT_EQ(kControlMsgSize, msg.msg_controllen); + ASSERT_EQ(0, msg.msg_flags); + ASSERT_EQ(0, close(fds[0])); pid_t crashing_pid = -1; int signal_fd = -1; @@ -765,8 +728,8 @@ TEST(ExceptionHandlerTest, ExternalDumper) { if (hdr->cmsg_type == SCM_RIGHTS) { const unsigned len = hdr->cmsg_len - (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); - ASSERT_EQ(len, sizeof(int)); - signal_fd = *((int *) CMSG_DATA(hdr)); + ASSERT_EQ(sizeof(int), len); + signal_fd = *(reinterpret_cast(CMSG_DATA(hdr))); } else if (hdr->cmsg_type == SCM_CREDENTIALS) { const struct ucred *cred = reinterpret_cast(CMSG_DATA(hdr)); @@ -783,15 +746,60 @@ TEST(ExceptionHandlerTest, ExternalDumper) { kCrashContextSize)); static const char b = 0; ASSERT_EQ(1U, (HANDLE_EINTR(write(signal_fd, &b, 1)))); - ASSERT_EQ(close(signal_fd), 0); + ASSERT_EQ(0, close(signal_fd)); - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGSEGV); + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); struct stat st; - ASSERT_EQ(stat(templ.c_str(), &st), 0); - ASSERT_GT(st.st_size, 0u); + ASSERT_EQ(0, stat(templ.c_str(), &st)); + ASSERT_GT(st.st_size, 0U); unlink(templ.c_str()); } + +TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) { + AutoTempDir temp_dir; + std::string path; + const int fd = CreateTMPFile(temp_dir.path(), &path); + ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, false, -1); + ASSERT_TRUE(handler.WriteMinidump()); + // Check by the size of the data written to the FD that a minidump was + // generated. + off_t size = lseek(fd, 0, SEEK_CUR); + ASSERT_GT(size, 0); + + // Generate another minidump. + ASSERT_TRUE(handler.WriteMinidump()); + size = lseek(fd, 0, SEEK_CUR); + ASSERT_GT(size, 0); +} + +TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) { + AutoTempDir temp_dir; + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, + NULL, false, -1); + ASSERT_TRUE(handler.WriteMinidump()); + + const MinidumpDescriptor& minidump_1 = handler.minidump_descriptor(); + struct stat st; + ASSERT_EQ(0, stat(minidump_1.path(), &st)); + ASSERT_GT(st.st_size, 0U); + std::string minidump_1_path(minidump_1.path()); + // Check it is a valid minidump. + Minidump minidump1(minidump_1_path); + ASSERT_TRUE(minidump1.Read()); + unlink(minidump_1.path()); + + // Generate another minidump, it should go to a different file. + ASSERT_TRUE(handler.WriteMinidump()); + const MinidumpDescriptor& minidump_2 = handler.minidump_descriptor(); + ASSERT_EQ(0, stat(minidump_2.path(), &st)); + ASSERT_GT(st.st_size, 0U); + std::string minidump_2_path(minidump_2.path()); + // Check it is a valid minidump. + Minidump minidump2(minidump_2_path); + ASSERT_TRUE(minidump2.Read()); + unlink(minidump_2.path()); + + // We expect 2 distinct files. + ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str()); +} diff --git a/src/client/linux/handler/minidump_descriptor.cc b/src/client/linux/handler/minidump_descriptor.cc new file mode 100644 index 00000000..130764af --- /dev/null +++ b/src/client/linux/handler/minidump_descriptor.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2012 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "client/linux/handler/minidump_descriptor.h" + +#include "common/linux/guid_creator.h" + +namespace google_breakpad { + +MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor) + : fd_(descriptor.fd_), + directory_(descriptor.directory_), + c_path_(NULL) { + // The copy constructor is not allowed to be called on a MinidumpDescriptor + // with a valid path_, as getting its c_path_ would require the heap which + // can cause problems in compromised environments. + assert(descriptor.path_.empty()); +} + +void MinidumpDescriptor::UpdatePath() { + assert(fd_ == -1 && !directory_.empty()); + + GUID guid; + char guid_str[kGUIDStringLength + 1]; + bool r = CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str)); + assert(r); + + path_.clear(); + path_ = directory_ + "/" + guid_str + ".dmp"; + c_path_ = path_.c_str(); +} + +} // namespace google_breakpad diff --git a/src/client/linux/handler/minidump_descriptor.h b/src/client/linux/handler/minidump_descriptor.h new file mode 100644 index 00000000..18e2cb41 --- /dev/null +++ b/src/client/linux/handler/minidump_descriptor.h @@ -0,0 +1,84 @@ +// Copyright (c) 2012 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ +#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ + +#include +#include + +// The MinidumpDescriptor describes how to access a minidump: it can contain +// either a file descriptor or a path. +// Note that when using files, it is created with the path to a directory. +// The actual path where the minidump is generated is created by this class. +namespace google_breakpad { + +class MinidumpDescriptor { + public: + MinidumpDescriptor() : fd_(-1) {} + + explicit MinidumpDescriptor(const std::string& directory) + : fd_(-1), + directory_(directory), + c_path_(NULL) { + assert(!directory.empty()); + } + + explicit MinidumpDescriptor(int fd) : fd_(fd), c_path_(NULL) { + assert(fd != -1); + } + + explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor); + + bool IsFD() const { return fd_ != -1; } + + int fd() const { return fd_; } + + const char* path() const { return c_path_; } + + // Updates the path so it is unique. + // Should be called from a normal context: this methods uses the heap. + void UpdatePath(); + + private: + // The file descriptor where the minidump is generated. + const int fd_; + + // The directory where the minidump should be generated. + const std::string directory_; + // The full path to the generated minidump. + std::string path_; + // The C string of |path_|. Precomputed so it can be access from a compromised + // context. + const char* c_path_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ -- cgit v1.2.1