mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2025-12-29 10:45:28 +01:00
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:
parent
cec12872c4
commit
4621ee0691
14 changed files with 930 additions and 18 deletions
|
|
@ -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_);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue