From 4621ee06914b2ebe963c93ea78fabf982cf670df Mon Sep 17 00:00:00 2001 From: "ted.mielczarek" Date: Thu, 23 Sep 2010 14:55:50 +0000 Subject: Write a window of memory around the instruction pointer from the crashing thread to the minidump on OS X. R=nealsid at http://breakpad.appspot.com/200001/show git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@699 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/client/mac/handler/minidump_generator.cc | 122 ++++++++++++++++++++++++++- src/client/mac/handler/minidump_generator.h | 13 ++- 2 files changed, 132 insertions(+), 3 deletions(-) (limited to 'src/client/mac/handler') diff --git a/src/client/mac/handler/minidump_generator.cc b/src/client/mac/handler/minidump_generator.cc index 0c54cf42..73086743 100644 --- a/src/client/mac/handler/minidump_generator.cc +++ b/src/client/mac/handler/minidump_generator.cc @@ -27,6 +27,7 @@ // (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 #include @@ -64,7 +65,8 @@ MinidumpGenerator::MinidumpGenerator() exception_thread_(0), crashing_task_(mach_task_self()), handler_thread_(mach_thread_self()), - dynamic_images_(NULL) { + dynamic_images_(NULL), + memory_blocks_(&allocator_) { GatherSystemInformation(); } @@ -79,7 +81,8 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, exception_thread_(0), crashing_task_(crashing_task), handler_thread_(handler_thread), - dynamic_images_(NULL) { + dynamic_images_(NULL), + memory_blocks_(&allocator_) { if (crashing_task != mach_task_self()) { dynamic_images_ = new DynamicImages(crashing_task_); } else { @@ -173,6 +176,7 @@ string MinidumpGenerator::UniqueNameInDirectory(const string &dir, bool MinidumpGenerator::Write(const char *path) { WriteStreamFN writers[] = { &MinidumpGenerator::WriteThreadListStream, + &MinidumpGenerator::WriteMemoryListStream, &MinidumpGenerator::WriteSystemInfoStream, &MinidumpGenerator::WriteModuleListStream, &MinidumpGenerator::WriteMiscInfoStream, @@ -514,6 +518,8 @@ bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id, if (!WriteStack(state, &thread->stack)) return false; + memory_blocks_.push_back(thread->stack); + if (!WriteContext(state, &thread->thread_context)) return false; @@ -566,6 +572,118 @@ bool MinidumpGenerator::WriteThreadListStream( return true; } +bool MinidumpGenerator::WriteMemoryListStream( + MDRawDirectory *memory_list_stream) { + TypedMDRVA list(&writer_); + + // If the dump has an exception, include some memory around the + // instruction pointer. + const size_t kIPMemorySize = 256; // bytes + bool have_ip_memory = false; + MDMemoryDescriptor ip_memory_d; + if (exception_thread_ && exception_type_) { + breakpad_thread_state_data_t state; + mach_msg_type_number_t stateCount + = static_cast(sizeof(state)); + + if (thread_get_state(exception_thread_, + BREAKPAD_MACHINE_THREAD_STATE, + state, + &stateCount) == KERN_SUCCESS) { + u_int64_t ip = CurrentPCForStack(state); + // Bound it to the upper and lower bounds of the region + // it's contained within. If it's not in a known memory region, + // don't bother trying to write it. + mach_vm_address_t addr = ip; + mach_vm_size_t size; + natural_t nesting_level = 0; + vm_region_submap_info_64 info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + + kern_return_t ret = + mach_vm_region_recurse(crashing_task_, + &addr, + &size, + &nesting_level, + (vm_region_recurse_info_t)&info, + &info_count); + if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) { + // Try to get 128 bytes before and after the IP, but + // settle for whatever's available. + ip_memory_d.start_of_memory_range = + std::max(uintptr_t(addr), + uintptr_t(ip - (kIPMemorySize / 2))); + uintptr_t end_of_range = + std::min(uintptr_t(ip + (kIPMemorySize / 2)), + uintptr_t(addr + size)); + ip_memory_d.memory.data_size = + end_of_range - ip_memory_d.start_of_memory_range; + have_ip_memory = true; + // This needs to get appended to the list even though + // the memory bytes aren't filled in yet so the entire + // list can be written first. The memory bytes will get filled + // in after the memory list is written. + memory_blocks_.push_back(ip_memory_d); + } + } + } + + // Now fill in the memory list and write it. + unsigned memory_count = memory_blocks_.size(); + if (!list.AllocateObjectAndArray(memory_count, + sizeof(MDMemoryDescriptor))) + return false; + + memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM; + memory_list_stream->location = list.location(); + + list.get()->number_of_memory_ranges = memory_count; + + unsigned int i; + for (i = 0; i < memory_count; ++i) { + list.CopyIndexAfterObject(i++, &memory_blocks_[i], + sizeof(MDMemoryDescriptor)); + } + + if (have_ip_memory) { + // Now read the memory around the instruction pointer. + UntypedMDRVA ip_memory(&writer_); + if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) + return false; + + if (dynamic_images_) { + // Out-of-process. + kern_return_t kr; + + void *memory = + ReadTaskMemory( + crashing_task_, + reinterpret_cast(ip_memory_d.start_of_memory_range), + ip_memory_d.memory.data_size, + &kr); + + if (memory == NULL) { + return false; + } + + ip_memory.Copy(memory, ip_memory_d.memory.data_size); + free(memory); + } else { + // In-process, just copy from local memory. + ip_memory.Copy( + reinterpret_cast(ip_memory_d.start_of_memory_range), + ip_memory_d.memory.data_size); + } + + ip_memory_d.memory = ip_memory.location(); + // Write this again now that the data location is filled in. + list.CopyIndexAfterObject(i - 1, &ip_memory_d, + sizeof(MDMemoryDescriptor)); + } + + return true; +} + bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) { TypedMDRVA exception(&writer_); diff --git a/src/client/mac/handler/minidump_generator.h b/src/client/mac/handler/minidump_generator.h index b065fba9..39f28402 100644 --- a/src/client/mac/handler/minidump_generator.h +++ b/src/client/mac/handler/minidump_generator.h @@ -37,8 +37,9 @@ #include #include "client/minidump_file_writer.h" -#include "google_breakpad/common/minidump_format.h" +#include "common/memory.h" #include "common/mac/macho_utilities.h" +#include "google_breakpad/common/minidump_format.h" #include "dynamic_images.h" @@ -119,6 +120,7 @@ class MinidumpGenerator { // Stream writers bool WriteThreadListStream(MDRawDirectory *thread_list_stream); + bool WriteMemoryListStream(MDRawDirectory *memory_list_stream); bool WriteExceptionStream(MDRawDirectory *exception_stream); bool WriteSystemInfoStream(MDRawDirectory *system_info_stream); bool WriteModuleListStream(MDRawDirectory *module_list_stream); @@ -165,6 +167,15 @@ class MinidumpGenerator { // Information about dynamically loaded code DynamicImages *dynamic_images_; + + // PageAllocator makes it possible to allocate memory + // directly from the system, even while handling an exception. + mutable PageAllocator allocator_; + + // Blocks of memory written to the dump. These are all currently + // written while writing the thread list stream, but saved here + // so a memory list stream can be written afterwards. + wasteful_vector memory_blocks_; }; } // namespace google_breakpad -- cgit v1.2.1