diff options
Diffstat (limited to 'src/client/linux')
-rw-r--r-- | src/client/linux/handler/exception_handler.cc | 28 | ||||
-rw-r--r-- | src/client/linux/handler/exception_handler.h | 11 | ||||
-rw-r--r-- | src/client/linux/handler/exception_handler_unittest.cc | 81 | ||||
-rw-r--r-- | src/client/linux/minidump_writer/minidump_writer.cc | 58 | ||||
-rw-r--r-- | src/client/linux/minidump_writer/minidump_writer.h | 26 | ||||
-rw-r--r-- | src/client/linux/minidump_writer/minidump_writer_unittest.cc | 79 |
6 files changed, 265 insertions, 18 deletions
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index 490b6eb9..6f0b8de4 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -495,13 +495,15 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, crashing_process, context, context_size, - mapping_list_); + mapping_list_, + app_memory_list_); } return google_breakpad::WriteMinidump(minidump_descriptor_.path(), crashing_process, context, context_size, - mapping_list_); + mapping_list_, + app_memory_list_); } // static @@ -562,4 +564,26 @@ void ExceptionHandler::AddMappingInfo(const string& name, mapping_list_.push_back(mapping); } +void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { + AppMemoryList::iterator iter = + std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); + if (iter != app_memory_list_.end()) { + // Don't allow registering the same pointer twice. + return; + } + + AppMemory app_memory; + app_memory.ptr = ptr; + app_memory.length = length; + app_memory_list_.push_back(app_memory); +} + +void ExceptionHandler::UnregisterAppMemory(void* ptr) { + AppMemoryList::iterator iter = + std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); + if (iter != app_memory_list_.end()) { + app_memory_list_.erase(iter); + } +} + } // namespace google_breakpad diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 95817424..68953adf 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -193,6 +193,13 @@ class ExceptionHandler { size_t mapping_size, size_t file_offset); + // Register a block of memory of length bytes starting at address ptr + // to be copied to the minidump when a crash happens. + void RegisterAppMemory(void* ptr, size_t length); + + // Unregister a block of memory that was registered with RegisterAppMemory. + void UnregisterAppMemory(void* ptr); + // Force signal handling for the specified signal. bool SimulateSignalDelivery(int sig); private: @@ -238,6 +245,10 @@ class ExceptionHandler { // Callers can add extra info about mappings for cases where the // dumper code cannot extract enough information from /proc/<pid>/maps. MappingList mapping_list_; + + // Callers can request additional memory regions to be included in + // the dump. + AppMemoryList app_memory_list_; }; } // namespace google_breakpad diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc index 6de9b8d4..341d2fb0 100644 --- a/src/client/linux/handler/exception_handler_unittest.cc +++ b/src/client/linux/handler/exception_handler_unittest.cc @@ -939,6 +939,85 @@ TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) { ASSERT_TRUE(minidump2.Read()); unlink(minidump_2.path()); - // We expect 2 distinct files. + // 2 distinct files should be produced. ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str()); } + +// Test that an additional memory region can be added to the minidump. +TEST(ExceptionHandlerTest, AdditionalMemory) { + const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); + + // Get some heap memory. + u_int8_t* memory = new u_int8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); + ASSERT_TRUE(memory); + + // Stick some data into the memory so the contents can be verified. + for (u_int32_t i = 0; i < kMemorySize; ++i) { + memory[i] = i % 255; + } + + AutoTempDir temp_dir; + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + + // Add the memory region to the list of memory to be included. + handler.RegisterAppMemory(memory, kMemorySize); + handler.WriteMinidump(); + + const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); + + // Read the minidump. Ensure that the memory region is present + Minidump minidump(minidump_desc.path()); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemoryAddress, region->GetBase()); + EXPECT_EQ(kMemorySize, region->GetSize()); + + // Verify memory contents. + EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); + + delete[] memory; +} + +// Test that a memory region that was previously registered +// can be unregistered. +TEST(ExceptionHandlerTest, AdditionalMemoryRemove) { + const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); + + // Get some heap memory. + u_int8_t* memory = new u_int8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); + ASSERT_TRUE(memory); + + AutoTempDir temp_dir; + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + + // Add the memory region to the list of memory to be included. + handler.RegisterAppMemory(memory, kMemorySize); + + // ...and then remove it + handler.UnregisterAppMemory(memory); + handler.WriteMinidump(); + + const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); + + // Read the minidump. Ensure that the memory region is not present. + Minidump minidump(minidump_desc.path()); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + EXPECT_FALSE(region); + + delete[] memory; +} diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index eb2e6d64..b081b298 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -76,6 +76,7 @@ namespace { +using google_breakpad::AppMemoryList; using google_breakpad::ExceptionHandler; using google_breakpad::LineReader; using google_breakpad::LinuxDumper; @@ -381,6 +382,7 @@ class MinidumpWriter { int minidump_fd, const ExceptionHandler::CrashContext* context, const MappingList& mappings, + const AppMemoryList& appmem, LinuxDumper* dumper) : fd_(minidump_fd), path_(minidump_path), @@ -393,7 +395,8 @@ class MinidumpWriter { #endif dumper_(dumper), memory_blocks_(dumper_->allocator()), - mapping_list_(mappings) { + mapping_list_(mappings), + app_memory_list_(appmem) { // Assert there should be either a valid fd or a valid path, not both. assert(fd_ != -1 || minidump_path); assert(fd_ == -1 || !minidump_path); @@ -480,6 +483,9 @@ class MinidumpWriter { return false; dir.CopyIndex(dir_index++, &dirent); + if (!WriteAppMemory()) + return false; + if (!WriteMemoryListStream(&dirent)) return false; dir.CopyIndex(dir_index++, &dirent); @@ -781,6 +787,30 @@ class MinidumpWriter { return true; } + // Write application-provided memory regions. + bool WriteAppMemory() { + for (AppMemoryList::const_iterator iter = app_memory_list_.begin(); + iter != app_memory_list_.end(); + ++iter) { + uint8_t* data_copy = + reinterpret_cast<uint8_t*>(dumper_->allocator()->Alloc(iter->length)); + dumper_->CopyFromProcess(data_copy, GetCrashThread(), iter->ptr, + iter->length); + + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(iter->length)) { + return false; + } + memory.Copy(data_copy, iter->length); + MDMemoryDescriptor desc; + desc.start_of_memory_range = reinterpret_cast<uintptr_t>(iter->ptr); + desc.memory = memory.location(); + memory_blocks_.push_back(desc); + } + + return true; + } + static bool ShouldIncludeMapping(const MappingInfo& mapping) { if (mapping.name[0] == 0 || // only want modules with filenames. mapping.offset || // only want to include one mapping per shared lib. @@ -1361,6 +1391,9 @@ class MinidumpWriter { wasteful_vector<MDMemoryDescriptor> memory_blocks_; // Additional information about some mappings provided by the caller. const MappingList& mapping_list_; + // Additional memory regions to be included in the dump, + // provided by the caller. + const AppMemoryList& app_memory_list_; }; @@ -1368,7 +1401,8 @@ bool WriteMinidumpImpl(const char* minidump_path, int minidump_fd, pid_t crashing_process, const void* blob, size_t blob_size, - const MappingList& mappings) { + const MappingList& mappings, + const AppMemoryList& appmem) { if (blob_size != sizeof(ExceptionHandler::CrashContext)) return false; const ExceptionHandler::CrashContext* context = @@ -1378,7 +1412,8 @@ bool WriteMinidumpImpl(const char* minidump_path, reinterpret_cast<uintptr_t>(context->siginfo.si_addr)); dumper.set_crash_signal(context->siginfo.si_signo); dumper.set_crash_thread(context->tid); - MinidumpWriter writer(minidump_path, minidump_fd, context, mappings, &dumper); + MinidumpWriter writer(minidump_path, minidump_fd, context, mappings, + appmem, &dumper); if (!writer.Init()) return false; return writer.Dump(); @@ -1391,33 +1426,36 @@ namespace google_breakpad { bool WriteMinidump(const char* minidump_path, pid_t crashing_process, const void* blob, size_t blob_size) { return WriteMinidumpImpl(minidump_path, -1, crashing_process, blob, blob_size, - MappingList()); + MappingList(), AppMemoryList()); } bool WriteMinidump(int minidump_fd, pid_t crashing_process, const void* blob, size_t blob_size) { return WriteMinidumpImpl(NULL, minidump_fd, crashing_process, blob, blob_size, - MappingList()); + MappingList(), AppMemoryList()); } bool WriteMinidump(const char* minidump_path, pid_t crashing_process, const void* blob, size_t blob_size, - const MappingList& mappings) { + const MappingList& mappings, + const AppMemoryList& appmem) { return WriteMinidumpImpl(minidump_path, -1, crashing_process, blob, blob_size, - mappings); + mappings, appmem); } bool WriteMinidump(int minidump_fd, pid_t crashing_process, const void* blob, size_t blob_size, - const MappingList& mappings) { + const MappingList& mappings, + const AppMemoryList& appmem) { return WriteMinidumpImpl(NULL, minidump_fd, crashing_process, blob, blob_size, - mappings); + mappings, appmem); } bool WriteMinidump(const char* filename, const MappingList& mappings, + const AppMemoryList& appmem, LinuxDumper* dumper) { - MinidumpWriter writer(filename, -1, NULL, mappings, dumper); + MinidumpWriter writer(filename, -1, NULL, mappings, appmem, dumper); if (!writer.Init()) return false; return writer.Dump(); diff --git a/src/client/linux/minidump_writer/minidump_writer.h b/src/client/linux/minidump_writer/minidump_writer.h index 14da94b6..1370d938 100644 --- a/src/client/linux/minidump_writer/minidump_writer.h +++ b/src/client/linux/minidump_writer/minidump_writer.h @@ -51,6 +51,22 @@ struct MappingEntry { // A list of <MappingInfo, GUID> typedef std::list<MappingEntry> MappingList; +// These entries store a list of memory regions that the client wants included +// in the minidump. +struct AppMemory { + void* ptr; + size_t length; + + bool operator==(const struct AppMemory& other) const { + return ptr == other.ptr; + } + + bool operator==(const void* other) const { + return ptr == other; + } +}; +typedef std::list<AppMemory> AppMemoryList; + // Writes a minidump to the filesystem. These functions do not malloc nor use // libc functions which may. Thus, it can be used in contexts where the state // of the heap may be corrupt. @@ -67,16 +83,20 @@ bool WriteMinidump(const char* minidump_path, pid_t crashing_process, bool WriteMinidump(int minidump_fd, pid_t crashing_process, const void* blob, size_t blob_size); -// These overloads also allow passing a list of known mappings. +// These overloads also allow passing a list of known mappings and +// a list of additional memory regions to be included in the minidump. bool WriteMinidump(const char* minidump_path, pid_t crashing_process, const void* blob, size_t blob_size, - const MappingList& mappings); + const MappingList& mappings, + const AppMemoryList& appdata); bool WriteMinidump(int minidump_fd, pid_t crashing_process, const void* blob, size_t blob_size, - const MappingList& mappings); + const MappingList& mappings, + const AppMemoryList& appdata); bool WriteMinidump(const char* filename, const MappingList& mappings, + const AppMemoryList& appdata, LinuxDumper* dumper); } // namespace google_breakpad diff --git a/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/client/linux/minidump_writer/minidump_writer_unittest.cc index 7558bee4..0a6b07d1 100644 --- a/src/client/linux/minidump_writer/minidump_writer_unittest.cc +++ b/src/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -32,6 +32,7 @@ #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> +#include <ucontext.h> #include <unistd.h> #include <string> @@ -184,12 +185,13 @@ TEST(MinidumpWriterTest, MappingInfo) { strcpy(info.name, kMemoryName); MappingList mappings; + AppMemoryList memory_list; MappingEntry mapping; mapping.first = info; memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); mappings.push_back(mapping); ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), - mappings)); + mappings, memory_list)); // Read the minidump. Load the module list, and ensure that // the mmap'ed |memory| is listed with the given module name @@ -288,12 +290,13 @@ TEST(MinidumpWriterTest, MappingInfoContained) { strcpy(info.name, kMemoryName); MappingList mappings; + AppMemoryList memory_list; MappingEntry mapping; mapping.first = info; memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); mappings.push_back(mapping); ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context), - mappings)); + mappings, memory_list)); // Read the minidump. Load the module list, and ensure that // the mmap'ed |memory| is listed with the given module name @@ -410,3 +413,75 @@ TEST(MinidumpWriterTest, DeletedBinary) { module_identifier += "0"; EXPECT_EQ(module_identifier, module->debug_identifier()); } + +// Test that an additional memory region can be added to the minidump. +TEST(MinidumpWriterTest, AdditionalMemory) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); + + // Get some heap memory. + u_int8_t* memory = new u_int8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); + ASSERT_TRUE(memory); + + // Stick some data into the memory so the contents can be verified. + for (u_int32_t i = 0; i < kMemorySize; ++i) { + memory[i] = i % 255; + } + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + + // This needs a valid context for minidump writing to work, but getting + // a useful one from the child is too much work, so just use one from + // the parent since the child is just a forked copy anyway. + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + AutoTempDir temp_dir; + string templ = temp_dir.path() + "/minidump-writer-unittest"; + unlink(templ.c_str()); + + MappingList mappings; + AppMemoryList memory_list; + + // Add the memory region to the list of memory to be included. + AppMemory app_memory; + app_memory.ptr = memory; + app_memory.length = kMemorySize; + memory_list.push_back(app_memory); + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), + mappings, memory_list)); + + // Read the minidump. Ensure that the memory region is present + Minidump minidump(templ.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemoryAddress, region->GetBase()); + EXPECT_EQ(kMemorySize, region->GetSize()); + + // Verify memory contents. + EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); + + delete[] memory; + close(fds[1]); +} |