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
This commit is contained in:
ted.mielczarek 2010-09-23 14:55:50 +00:00
parent cec12872c4
commit 4621ee0691
14 changed files with 930 additions and 18 deletions

View file

@ -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 <algorithm>
#include <cstdio>
#include <mach/host_info.h>
@ -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<MDRawMemoryList> 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<mach_msg_type_number_t>(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<const void *>(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<const void *>(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<MDRawExceptionStream> exception(&writer_);

View file

@ -37,8 +37,9 @@
#include <string>
#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<MDMemoryDescriptor> memory_blocks_;
};
} // namespace google_breakpad