зеркало из https://github.com/mozilla/pjs.git
bug 360938 - Update airbag to svn tip. r=mento
This commit is contained in:
Родитель
23d197365f
Коммит
02735f726a
|
@ -50,35 +50,39 @@ dist_doc_DATA = \
|
|||
lib_LTLIBRARIES = src/libairbag.la
|
||||
|
||||
src_libairbag_la_SOURCES = \
|
||||
src/google/airbag_types.h \
|
||||
src/google/call_stack.h \
|
||||
src/google/minidump_processor.h \
|
||||
src/google/process_state.h \
|
||||
src/google/stack_frame.h \
|
||||
src/google/stack_frame_cpu.h \
|
||||
src/google/symbol_supplier.h \
|
||||
src/google_airbag/common/airbag_types.h \
|
||||
src/google_airbag/common/minidump_format.h \
|
||||
src/google_airbag/processor/call_stack.h \
|
||||
src/google_airbag/processor/memory_region.h \
|
||||
src/google_airbag/processor/minidump.h \
|
||||
src/google_airbag/processor/minidump_processor.h \
|
||||
src/google_airbag/processor/process_state.h \
|
||||
src/google_airbag/processor/stack_frame.h \
|
||||
src/google_airbag/processor/stack_frame_cpu.h \
|
||||
src/google_airbag/processor/stackwalker.h \
|
||||
src/google_airbag/processor/symbol_supplier.h \
|
||||
src/processor/address_map.h \
|
||||
src/processor/address_map-inl.h \
|
||||
src/processor/call_stack.cc \
|
||||
src/processor/contained_range_map.h \
|
||||
src/processor/contained_range_map-inl.h \
|
||||
src/processor/linked_ptr.h \
|
||||
src/processor/memory_region.h \
|
||||
src/processor/minidump.cc \
|
||||
src/processor/minidump.h \
|
||||
src/processor/minidump_format.h \
|
||||
src/processor/minidump_processor.cc \
|
||||
src/processor/pathname_stripper.cc \
|
||||
src/processor/pathname_stripper.h \
|
||||
src/processor/postfix_evaluator.h \
|
||||
src/processor/postfix_evaluator-inl.h \
|
||||
src/processor/process_state.cc \
|
||||
src/processor/range_map.h \
|
||||
src/processor/range_map-inl.h \
|
||||
src/processor/scoped_ptr.h \
|
||||
src/processor/simple_symbol_supplier.cc \
|
||||
src/processor/simple_symbol_supplier.h \
|
||||
src/processor/source_line_resolver.cc \
|
||||
src/processor/source_line_resolver.h \
|
||||
src/processor/stack_frame_info.h \
|
||||
src/processor/stackwalker.cc \
|
||||
src/processor/stackwalker.h \
|
||||
src/processor/stackwalker_ppc.cc \
|
||||
src/processor/stackwalker_ppc.h \
|
||||
src/processor/stackwalker_x86.cc \
|
||||
|
@ -96,6 +100,7 @@ check_PROGRAMS = \
|
|||
src/processor/address_map_unittest \
|
||||
src/processor/contained_range_map_unittest \
|
||||
src/processor/minidump_processor_unittest \
|
||||
src/processor/pathname_stripper_unittest \
|
||||
src/processor/postfix_evaluator_unittest \
|
||||
src/processor/range_map_unittest \
|
||||
src/processor/source_line_resolver_unittest
|
||||
|
@ -130,6 +135,11 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
src/processor/stackwalker_x86.lo \
|
||||
src/processor/source_line_resolver.lo
|
||||
|
||||
src_processor_pathname_stripper_unittest_SOURCES = \
|
||||
src/processor/pathname_stripper_unittest.cc
|
||||
src_processor_pathname_stripper_unittest_LDADD = \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_postfix_evaluator_unittest_SOURCES = \
|
||||
src/processor/postfix_evaluator_unittest.cc
|
||||
|
||||
|
@ -165,6 +175,10 @@ src_processor_minidump_stackwalk_SOURCES = \
|
|||
src_processor_minidump_stackwalk_LDADD = \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/simple_symbol_supplier.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo \
|
||||
|
|
|
@ -73,6 +73,7 @@ bin_PROGRAMS = src/processor/minidump_dump$(EXEEXT) \
|
|||
check_PROGRAMS = src/processor/address_map_unittest$(EXEEXT) \
|
||||
src/processor/contained_range_map_unittest$(EXEEXT) \
|
||||
src/processor/minidump_processor_unittest$(EXEEXT) \
|
||||
src/processor/pathname_stripper_unittest$(EXEEXT) \
|
||||
src/processor/postfix_evaluator_unittest$(EXEEXT) \
|
||||
src/processor/range_map_unittest$(EXEEXT) \
|
||||
src/processor/source_line_resolver_unittest$(EXEEXT) \
|
||||
|
@ -111,7 +112,9 @@ src_libairbag_la_LIBADD =
|
|||
am__dirstamp = $(am__leading_dot)dirstamp
|
||||
am_src_libairbag_la_OBJECTS = src/processor/call_stack.lo \
|
||||
src/processor/minidump.lo src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/simple_symbol_supplier.lo \
|
||||
src/processor/source_line_resolver.lo \
|
||||
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo
|
||||
|
@ -152,9 +155,19 @@ src_processor_minidump_stackwalk_OBJECTS = \
|
|||
$(am_src_processor_minidump_stackwalk_OBJECTS)
|
||||
src_processor_minidump_stackwalk_DEPENDENCIES = \
|
||||
src/processor/call_stack.lo src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/simple_symbol_supplier.lo \
|
||||
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo \
|
||||
src/processor/source_line_resolver.lo
|
||||
am_src_processor_pathname_stripper_unittest_OBJECTS = \
|
||||
src/processor/pathname_stripper_unittest.$(OBJEXT)
|
||||
src_processor_pathname_stripper_unittest_OBJECTS = \
|
||||
$(am_src_processor_pathname_stripper_unittest_OBJECTS)
|
||||
src_processor_pathname_stripper_unittest_DEPENDENCIES = \
|
||||
src/processor/pathname_stripper.lo
|
||||
am_src_processor_postfix_evaluator_unittest_OBJECTS = \
|
||||
src/processor/postfix_evaluator_unittest.$(OBJEXT)
|
||||
src_processor_postfix_evaluator_unittest_OBJECTS = \
|
||||
|
@ -206,6 +219,7 @@ SOURCES = $(src_libairbag_la_SOURCES) \
|
|||
$(src_processor_minidump_dump_SOURCES) \
|
||||
$(src_processor_minidump_processor_unittest_SOURCES) \
|
||||
$(src_processor_minidump_stackwalk_SOURCES) \
|
||||
$(src_processor_pathname_stripper_unittest_SOURCES) \
|
||||
$(src_processor_postfix_evaluator_unittest_SOURCES) \
|
||||
$(src_processor_range_map_unittest_SOURCES) \
|
||||
$(src_processor_source_line_resolver_unittest_SOURCES) \
|
||||
|
@ -216,6 +230,7 @@ DIST_SOURCES = $(src_libairbag_la_SOURCES) \
|
|||
$(src_processor_minidump_dump_SOURCES) \
|
||||
$(src_processor_minidump_processor_unittest_SOURCES) \
|
||||
$(src_processor_minidump_stackwalk_SOURCES) \
|
||||
$(src_processor_pathname_stripper_unittest_SOURCES) \
|
||||
$(src_processor_postfix_evaluator_unittest_SOURCES) \
|
||||
$(src_processor_range_map_unittest_SOURCES) \
|
||||
$(src_processor_source_line_resolver_unittest_SOURCES) \
|
||||
|
@ -352,35 +367,39 @@ dist_doc_DATA = \
|
|||
|
||||
lib_LTLIBRARIES = src/libairbag.la
|
||||
src_libairbag_la_SOURCES = \
|
||||
src/google/airbag_types.h \
|
||||
src/google/call_stack.h \
|
||||
src/google/minidump_processor.h \
|
||||
src/google/process_state.h \
|
||||
src/google/stack_frame.h \
|
||||
src/google/stack_frame_cpu.h \
|
||||
src/google/symbol_supplier.h \
|
||||
src/google_airbag/common/airbag_types.h \
|
||||
src/google_airbag/common/minidump_format.h \
|
||||
src/google_airbag/processor/call_stack.h \
|
||||
src/google_airbag/processor/memory_region.h \
|
||||
src/google_airbag/processor/minidump.h \
|
||||
src/google_airbag/processor/minidump_processor.h \
|
||||
src/google_airbag/processor/process_state.h \
|
||||
src/google_airbag/processor/stack_frame.h \
|
||||
src/google_airbag/processor/stack_frame_cpu.h \
|
||||
src/google_airbag/processor/stackwalker.h \
|
||||
src/google_airbag/processor/symbol_supplier.h \
|
||||
src/processor/address_map.h \
|
||||
src/processor/address_map-inl.h \
|
||||
src/processor/call_stack.cc \
|
||||
src/processor/contained_range_map.h \
|
||||
src/processor/contained_range_map-inl.h \
|
||||
src/processor/linked_ptr.h \
|
||||
src/processor/memory_region.h \
|
||||
src/processor/minidump.cc \
|
||||
src/processor/minidump.h \
|
||||
src/processor/minidump_format.h \
|
||||
src/processor/minidump_processor.cc \
|
||||
src/processor/pathname_stripper.cc \
|
||||
src/processor/pathname_stripper.h \
|
||||
src/processor/postfix_evaluator.h \
|
||||
src/processor/postfix_evaluator-inl.h \
|
||||
src/processor/process_state.cc \
|
||||
src/processor/range_map.h \
|
||||
src/processor/range_map-inl.h \
|
||||
src/processor/scoped_ptr.h \
|
||||
src/processor/simple_symbol_supplier.cc \
|
||||
src/processor/simple_symbol_supplier.h \
|
||||
src/processor/source_line_resolver.cc \
|
||||
src/processor/source_line_resolver.h \
|
||||
src/processor/stack_frame_info.h \
|
||||
src/processor/stackwalker.cc \
|
||||
src/processor/stackwalker.h \
|
||||
src/processor/stackwalker_ppc.cc \
|
||||
src/processor/stackwalker_ppc.h \
|
||||
src/processor/stackwalker_x86.cc \
|
||||
|
@ -411,6 +430,12 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
src/processor/stackwalker_x86.lo \
|
||||
src/processor/source_line_resolver.lo
|
||||
|
||||
src_processor_pathname_stripper_unittest_SOURCES = \
|
||||
src/processor/pathname_stripper_unittest.cc
|
||||
|
||||
src_processor_pathname_stripper_unittest_LDADD = \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_postfix_evaluator_unittest_SOURCES = \
|
||||
src/processor/postfix_evaluator_unittest.cc
|
||||
|
||||
|
@ -447,6 +472,10 @@ src_processor_minidump_stackwalk_SOURCES = \
|
|||
src_processor_minidump_stackwalk_LDADD = \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/simple_symbol_supplier.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo \
|
||||
|
@ -556,8 +585,13 @@ src/processor/minidump.lo: src/processor/$(am__dirstamp) \
|
|||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/minidump_processor.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/pathname_stripper.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/process_state.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/simple_symbol_supplier.lo: \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/source_line_resolver.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/stackwalker.lo: src/processor/$(am__dirstamp) \
|
||||
|
@ -642,6 +676,12 @@ src/processor/minidump_stackwalk.$(OBJEXT): \
|
|||
src/processor/minidump_stackwalk$(EXEEXT): $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/minidump_stackwalk$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_minidump_stackwalk_LDFLAGS) $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_LDADD) $(LIBS)
|
||||
src/processor/pathname_stripper_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/pathname_stripper_unittest$(EXEEXT): $(src_processor_pathname_stripper_unittest_OBJECTS) $(src_processor_pathname_stripper_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/pathname_stripper_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_pathname_stripper_unittest_LDFLAGS) $(src_processor_pathname_stripper_unittest_OBJECTS) $(src_processor_pathname_stripper_unittest_LDADD) $(LIBS)
|
||||
src/processor/postfix_evaluator_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
|
@ -680,10 +720,15 @@ mostlyclean-compile:
|
|||
-rm -f src/processor/minidump_processor.lo
|
||||
-rm -f src/processor/minidump_processor_unittest.$(OBJEXT)
|
||||
-rm -f src/processor/minidump_stackwalk.$(OBJEXT)
|
||||
-rm -f src/processor/pathname_stripper.$(OBJEXT)
|
||||
-rm -f src/processor/pathname_stripper.lo
|
||||
-rm -f src/processor/pathname_stripper_unittest.$(OBJEXT)
|
||||
-rm -f src/processor/postfix_evaluator_unittest.$(OBJEXT)
|
||||
-rm -f src/processor/process_state.$(OBJEXT)
|
||||
-rm -f src/processor/process_state.lo
|
||||
-rm -f src/processor/range_map_unittest.$(OBJEXT)
|
||||
-rm -f src/processor/simple_symbol_supplier.$(OBJEXT)
|
||||
-rm -f src/processor/simple_symbol_supplier.lo
|
||||
-rm -f src/processor/source_line_resolver.$(OBJEXT)
|
||||
-rm -f src/processor/source_line_resolver.lo
|
||||
-rm -f src/processor/source_line_resolver_unittest.$(OBJEXT)
|
||||
|
@ -706,9 +751,12 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor_unittest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_stackwalk.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper_unittest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/postfix_evaluator_unittest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/process_state.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_unittest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/simple_symbol_supplier.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver_unittest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker.Plo@am__quote@
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// minidump_file_writer-inl.h: Minidump file writer implementation.
|
||||
//
|
||||
// See minidump_file_writer.h for documentation.
|
||||
|
||||
#ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__
|
||||
#define CLIENT_MINIDUMP_FILE_WRITER_INL_H__
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "client/minidump_file_writer.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::Allocate() {
|
||||
allocation_state_ = SINGLE_OBJECT;
|
||||
return UntypedMDRVA::Allocate(sizeof(MDType));
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::Allocate(size_t additional) {
|
||||
allocation_state_ = SINGLE_OBJECT;
|
||||
return UntypedMDRVA::Allocate(sizeof(MDType) + additional);
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) {
|
||||
assert(count);
|
||||
allocation_state_ = ARRAY;
|
||||
return UntypedMDRVA::Allocate(sizeof(MDType) * count);
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(unsigned int count,
|
||||
size_t size) {
|
||||
assert(count && size);
|
||||
allocation_state_ = SINGLE_OBJECT_WITH_ARRAY;
|
||||
return UntypedMDRVA::Allocate(sizeof(MDType) + count * size);
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) {
|
||||
assert(allocation_state_ == ARRAY);
|
||||
return writer_->Copy(position_ + index * sizeof(MDType), item,
|
||||
sizeof(MDType));
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index,
|
||||
void *src, size_t size) {
|
||||
assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY);
|
||||
return writer_->Copy(position_ + sizeof(MDType) + index * size, src, size);
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::Flush() {
|
||||
return writer_->Copy(position_, &data_, sizeof(MDType));
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // CLIENT_MINIDUMP_FILE_WRITER_INL_H__
|
|
@ -0,0 +1,249 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// minidump_file_writer.cc: Minidump file writer implementation.
|
||||
//
|
||||
// See minidump_file_writer.h for documentation.
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "client/minidump_file_writer-inl.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
MinidumpFileWriter::MinidumpFileWriter() : file_(-1), position_(0), size_(0) {
|
||||
}
|
||||
|
||||
MinidumpFileWriter::~MinidumpFileWriter() {
|
||||
Close();
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::Open(const std::string &path) {
|
||||
assert(file_ == -1);
|
||||
file_ = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
|
||||
return file_ != -1;
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::Close() {
|
||||
bool result = true;
|
||||
|
||||
if (file_ != -1) {
|
||||
ftruncate(file_, position_);
|
||||
result = close(file_) == 0;
|
||||
file_ = -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::WriteString(const wchar_t *str,
|
||||
unsigned int length,
|
||||
MDLocationDescriptor *location) {
|
||||
assert(str);
|
||||
assert(location);
|
||||
// Calculate the mdstring length by either limiting to |length| as passed in
|
||||
// or by finding the location of the NULL character.
|
||||
if (!length)
|
||||
length = INT_MAX;
|
||||
|
||||
unsigned int mdstring_length = 0;
|
||||
for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) {
|
||||
}
|
||||
|
||||
// Allocate the string buffer
|
||||
TypedMDRVA<MDString> mdstring(this);
|
||||
|
||||
if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(u_int16_t)))
|
||||
return false;
|
||||
|
||||
// Set length excluding the NULL
|
||||
mdstring.get()->length = mdstring_length * sizeof(u_int16_t);
|
||||
|
||||
u_int16_t ch;
|
||||
bool result = true;
|
||||
|
||||
if (sizeof(wchar_t) == sizeof(u_int16_t)) {
|
||||
// Shortcut if wchar_t is the same size as MDString's buffer
|
||||
result = mdstring.Copy(str, mdstring.get()->length);
|
||||
} else {
|
||||
// Copy the string character by character
|
||||
for (unsigned int c = 0; c < mdstring_length && result == true; c++) {
|
||||
ch = str[c];
|
||||
// TODO: For the UTF-32->UTF-16 conversion, it's possible that there
|
||||
// are characters that will require more than one UTF-16 character to
|
||||
// represent it. Fully supporting this will require a more sophisticated
|
||||
// calculation of the size of the resulting string and for converting the
|
||||
// UTF-32 character into the two UTF-16 characters.
|
||||
result = mdstring.CopyIndexAfterObject(c, &ch, sizeof(ch));
|
||||
}
|
||||
}
|
||||
|
||||
// NULL terminate
|
||||
if (result) {
|
||||
ch = 0;
|
||||
result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
|
||||
|
||||
if (result)
|
||||
*location = mdstring.location();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
|
||||
MDLocationDescriptor *location) {
|
||||
assert(str);
|
||||
assert(location);
|
||||
// Calculate the mdstring length by either limiting to |length| as passed in
|
||||
// or by finding the location of the NULL character.
|
||||
if (!length)
|
||||
length = INT_MAX;
|
||||
|
||||
unsigned int mdstring_length = 0;
|
||||
for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) {
|
||||
}
|
||||
|
||||
// Allocate the string buffer
|
||||
TypedMDRVA<MDString> mdstring(this);
|
||||
|
||||
if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(u_int16_t)))
|
||||
return false;
|
||||
|
||||
// Set length excluding the NULL
|
||||
mdstring.get()->length = mdstring_length * sizeof(u_int16_t);
|
||||
|
||||
u_int16_t ch;
|
||||
bool result = true;
|
||||
|
||||
// Copy the string character by character
|
||||
for (unsigned int c = 0; c < mdstring_length && result == true; c++) {
|
||||
ch = str[c];
|
||||
// TODO: For the UTF-8->UTF-16 conversion, it's possible that there are
|
||||
// characters that will convert one or more UTF-8 character into a single
|
||||
// UTF-16 character. Fully supporting this will require a more
|
||||
// sophisticated calculation of the size of the resulting string and for
|
||||
// converting the UTF-8 characters into a UTF-16 character.
|
||||
result = mdstring.CopyIndexAfterObject(c, &ch, sizeof(ch));
|
||||
}
|
||||
|
||||
// NULL terminate
|
||||
if (result) {
|
||||
ch = 0;
|
||||
result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
|
||||
|
||||
if (result)
|
||||
*location = mdstring.location();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
|
||||
MDMemoryDescriptor *output) {
|
||||
assert(src);
|
||||
assert(output);
|
||||
UntypedMDRVA mem(this);
|
||||
|
||||
if (!mem.Allocate(size))
|
||||
return false;
|
||||
|
||||
if (!mem.Copy(src, mem.size()))
|
||||
return false;
|
||||
|
||||
output->start_of_memory_range = reinterpret_cast<u_int64_t>(src);
|
||||
output->memory = mem.location();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MDRVA MinidumpFileWriter::Allocate(size_t size) {
|
||||
assert(size);
|
||||
assert(file_ != -1);
|
||||
|
||||
size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
|
||||
|
||||
if (position_ + aligned_size > size_) {
|
||||
size_t growth = aligned_size;
|
||||
size_t minimal_growth = getpagesize();
|
||||
|
||||
// Ensure that the file grows by at least the size of a memory page
|
||||
if (growth < minimal_growth)
|
||||
growth = minimal_growth;
|
||||
|
||||
size_t new_size = size_ + growth;
|
||||
|
||||
if (ftruncate(file_, new_size) != 0)
|
||||
return kInvalidMDRVA;
|
||||
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
MDRVA current_position = position_;
|
||||
position_ += static_cast<MDRVA>(aligned_size);
|
||||
|
||||
return current_position;
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::Copy(MDRVA position, const void* src, ssize_t size) {
|
||||
assert(src);
|
||||
assert(size);
|
||||
assert(file_ != -1);
|
||||
|
||||
// Ensure that the data will fit in the allocated space
|
||||
if (size + position > size_)
|
||||
return false;
|
||||
|
||||
// Seek and write the data
|
||||
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position))
|
||||
if (write(file_, src, size) == size)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UntypedMDRVA::Allocate(size_t size) {
|
||||
assert(size_ == 0);
|
||||
size_ = size;
|
||||
position_ = writer_->Allocate(size_);
|
||||
return position_ != MinidumpFileWriter::kInvalidMDRVA;
|
||||
}
|
||||
|
||||
bool UntypedMDRVA::Copy(MDRVA position, const void *src, size_t size) {
|
||||
assert(src);
|
||||
assert(size);
|
||||
assert(position + size <= position_ + size_);
|
||||
return writer_->Copy(position, src, size);
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,218 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// minidump_file_writer.h: Implements file-based minidump generation
|
||||
|
||||
#ifndef CLIENT_MINIDUMP_FILE_WRITER_H__
|
||||
#define CLIENT_MINIDUMP_FILE_WRITER_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "google_airbag/common/minidump_format.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
class MinidumpFileWriter {
|
||||
public:
|
||||
// Invalid MDRVA (Minidump Relative Virtual Address)
|
||||
// returned on failed allocation
|
||||
static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1);
|
||||
|
||||
MinidumpFileWriter();
|
||||
~MinidumpFileWriter();
|
||||
|
||||
// Open |path| as the destination of the minidump data. Any existing file
|
||||
// will be overwritten.
|
||||
// Return true on success, or false on failure
|
||||
bool Open(const std::string &path);
|
||||
|
||||
// Close the current file
|
||||
// Return true on success, or false on failure
|
||||
bool Close();
|
||||
|
||||
// Write |str| to a MDString.
|
||||
// |str| is expected to be either UTF-16 or UTF-32 depending on the size
|
||||
// of wchar_t.
|
||||
// Maximum |length| of characters to copy from |str|, or specify 0 to use the
|
||||
// entire NULL terminated string. Copying will stop at the first NULL.
|
||||
// |location| the allocated location
|
||||
// Return true on success, or false on failure
|
||||
bool WriteString(const wchar_t *str, unsigned int length,
|
||||
MDLocationDescriptor *location);
|
||||
|
||||
// Similar to above with |str| as an UTF-8 encoded string
|
||||
bool WriteString(const char *str, unsigned int length,
|
||||
MDLocationDescriptor *location);
|
||||
|
||||
// Write |size| bytes starting at |src| into the current position.
|
||||
// Return true on success and set |output| to position, or false on failure
|
||||
bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output);
|
||||
|
||||
// Copies |size| bytes from |src| to |position|
|
||||
// Return true on success, or false on failure
|
||||
bool Copy(MDRVA position, const void *src, ssize_t size);
|
||||
|
||||
// Return the current position for writing to the minidump
|
||||
MDRVA position() const { return position_; }
|
||||
|
||||
private:
|
||||
friend class UntypedMDRVA;
|
||||
|
||||
// Allocates an area of |size| bytes.
|
||||
// Returns the position of the allocation, or kInvalidMDRVA if it was
|
||||
// unable to allocate the bytes.
|
||||
MDRVA Allocate(size_t size);
|
||||
|
||||
// The file descriptor for the output file
|
||||
int file_;
|
||||
|
||||
// Current position in buffer
|
||||
MDRVA position_;
|
||||
|
||||
// Current allocated size
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// Represents an untyped allocated chunk
|
||||
class UntypedMDRVA {
|
||||
public:
|
||||
explicit UntypedMDRVA(MinidumpFileWriter *writer)
|
||||
: writer_(writer),
|
||||
position_(writer->position()),
|
||||
size_(0) {}
|
||||
|
||||
// Allocates |size| bytes. Must not call more than once.
|
||||
// Return true on success, or false on failure
|
||||
bool Allocate(size_t size);
|
||||
|
||||
// Returns the current position or kInvalidMDRVA if allocation failed
|
||||
MDRVA position() const { return position_; }
|
||||
|
||||
// Number of bytes allocated
|
||||
size_t size() const { return size_; }
|
||||
|
||||
// Return size and position
|
||||
MDLocationDescriptor location() const {
|
||||
MDLocationDescriptor location = { size_, position_ };
|
||||
return location;
|
||||
}
|
||||
|
||||
// Copy |size| bytes starting at |src| into the minidump at |position|
|
||||
// Return true on success, or false on failure
|
||||
bool Copy(MDRVA position, const void *src, size_t size);
|
||||
|
||||
// Copy |size| bytes from |src| to the current position
|
||||
bool Copy(const void *src, size_t size) {
|
||||
return Copy(position_, src, size);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Writer we associate with
|
||||
MinidumpFileWriter *writer_;
|
||||
|
||||
// Position of the start of the data
|
||||
MDRVA position_;
|
||||
|
||||
// Allocated size
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// Represents a Minidump object chunk. Additional memory can be allocated at
|
||||
// the end of the object as a:
|
||||
// - single allocation
|
||||
// - Array of MDType objects
|
||||
// - A MDType object followed by an array
|
||||
template<typename MDType>
|
||||
class TypedMDRVA : public UntypedMDRVA {
|
||||
public:
|
||||
// Constructs an unallocated MDRVA
|
||||
explicit TypedMDRVA(MinidumpFileWriter *writer)
|
||||
: UntypedMDRVA(writer),
|
||||
data_(),
|
||||
allocation_state_(UNALLOCATED) {}
|
||||
|
||||
~TypedMDRVA() {
|
||||
// Ensure that the data_ object is written out
|
||||
if (allocation_state_ != ARRAY)
|
||||
Flush();
|
||||
}
|
||||
|
||||
// Address of object data_ of MDType. This is not declared const as the
|
||||
// typical usage will be to access the underlying |data_| object as to
|
||||
// alter its contents.
|
||||
MDType *get() { return &data_; }
|
||||
|
||||
// Allocates sizeof(MDType) bytes.
|
||||
// Must not call more than once.
|
||||
// Return true on success, or false on failure
|
||||
bool Allocate();
|
||||
|
||||
// Allocates sizeof(MDType) + |additional| bytes.
|
||||
// Must not call more than once.
|
||||
// Return true on success, or false on failure
|
||||
bool Allocate(size_t additional);
|
||||
|
||||
// Allocate an array of |count| elements of MDType.
|
||||
// Must not call more than once.
|
||||
// Return true on success, or false on failure
|
||||
bool AllocateArray(size_t count);
|
||||
|
||||
// Allocate an array of |count| elements of |size| after object of MDType
|
||||
// Must not call more than once.
|
||||
// Return true on success, or false on failure
|
||||
bool AllocateObjectAndArray(unsigned int count, size_t size);
|
||||
|
||||
// Copy |item| to |index|
|
||||
// Must have been allocated using AllocateArray().
|
||||
// Return true on success, or false on failure
|
||||
bool CopyIndex(unsigned int index, MDType *item);
|
||||
|
||||
// Copy |size| bytes starting at |str| to |index|
|
||||
// Must have been allocated using AllocateObjectAndArray().
|
||||
// Return true on success, or false on failure
|
||||
bool CopyIndexAfterObject(unsigned int index, void *src, size_t size);
|
||||
|
||||
// Write data_
|
||||
bool Flush();
|
||||
|
||||
private:
|
||||
enum AllocationState {
|
||||
UNALLOCATED = 0,
|
||||
SINGLE_OBJECT,
|
||||
ARRAY,
|
||||
SINGLE_OBJECT_WITH_ARRAY
|
||||
};
|
||||
|
||||
MDType data_;
|
||||
AllocationState allocation_state_;
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // CLIENT_MINIDUMP_FILE_WRITER_H__
|
|
@ -31,12 +31,23 @@
|
|||
|
||||
#include <cstdio>
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
#include "client/windows/handler/exception_handler.h"
|
||||
#include "common/windows/guid_string.h"
|
||||
#include "google_airbag/common/minidump_format.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
ExceptionHandler *ExceptionHandler::current_handler_ = NULL;
|
||||
HANDLE ExceptionHandler::handler_thread_ = NULL;
|
||||
CRITICAL_SECTION ExceptionHandler::handler_critical_section_;
|
||||
HANDLE ExceptionHandler::handler_start_semaphore_ = NULL;
|
||||
HANDLE ExceptionHandler::handler_finish_semaphore_ = NULL;
|
||||
ExceptionHandler *ExceptionHandler::requesting_handler_ = NULL;
|
||||
DWORD ExceptionHandler::requesting_thread_id_ = 0;
|
||||
EXCEPTION_POINTERS *ExceptionHandler::exception_info_ = NULL;
|
||||
bool ExceptionHandler::handler_return_value_ = false;
|
||||
|
||||
ExceptionHandler::ExceptionHandler(const wstring &dump_path,
|
||||
MinidumpCallback callback,
|
||||
|
@ -46,6 +57,22 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path,
|
|||
dump_path_(dump_path), dbghelp_module_(NULL),
|
||||
minidump_write_dump_(NULL), previous_handler_(current_handler_),
|
||||
previous_filter_(NULL) {
|
||||
if (!handler_thread_) {
|
||||
// The first time an ExceptionHandler is created, set up the handler
|
||||
// thread and the synchronization primitives.
|
||||
InitializeCriticalSection(&handler_critical_section_);
|
||||
handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
|
||||
DWORD thread_id;
|
||||
handler_thread_ = CreateThread(NULL, // lpThreadAttributes
|
||||
64 * 1024, // dwStackSize
|
||||
ExceptionHandlerThreadMain,
|
||||
NULL, // lpParameter
|
||||
0, // dwCreationFlags
|
||||
&thread_id);
|
||||
}
|
||||
|
||||
UpdateNextID();
|
||||
dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
|
||||
if (dbghelp_module_) {
|
||||
|
@ -66,18 +93,72 @@ ExceptionHandler::~ExceptionHandler() {
|
|||
SetUnhandledExceptionFilter(previous_filter_);
|
||||
current_handler_ = previous_handler_;
|
||||
}
|
||||
|
||||
if (previous_handler_ == NULL) {
|
||||
// When destroying the last ExceptionHandler, clean up the handler thread
|
||||
// and synchronization primitives.
|
||||
TerminateThread(handler_thread_, 1);
|
||||
handler_thread_ = NULL;
|
||||
DeleteCriticalSection(&handler_critical_section_);
|
||||
CloseHandle(handler_start_semaphore_);
|
||||
handler_start_semaphore_ = NULL;
|
||||
CloseHandle(handler_finish_semaphore_);
|
||||
handler_finish_semaphore_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
DWORD ExceptionHandler::ExceptionHandlerThreadMain(void *lpParameter) {
|
||||
while (true) {
|
||||
if (WaitForSingleObject(handler_start_semaphore_, INFINITE) ==
|
||||
WAIT_OBJECT_0) {
|
||||
// Perform the requested action.
|
||||
handler_return_value_ = requesting_handler_->WriteMinidumpWithException(
|
||||
requesting_thread_id_, exception_info_);
|
||||
|
||||
// Allow the requesting thread to proceed.
|
||||
ReleaseSemaphore(handler_finish_semaphore_, 1, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Not reached. This thread will be terminated by ExceptionHandler's
|
||||
// destructor.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// static
|
||||
LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) {
|
||||
if (!current_handler_->WriteMinidumpWithException(exinfo)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
return current_handler_->WriteMinidumpOnHandlerThread(exinfo) ?
|
||||
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS *exinfo) {
|
||||
EnterCriticalSection(&handler_critical_section_);
|
||||
|
||||
// Set up data to be passed in to the handler thread.
|
||||
requesting_handler_ = this;
|
||||
requesting_thread_id_ = GetCurrentThreadId();
|
||||
exception_info_ = exinfo;
|
||||
|
||||
// This causes the handler thread to call WriteMinidumpWithException.
|
||||
ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
|
||||
|
||||
// Wait until WriteMinidumpWithException is done and collect its return value.
|
||||
WaitForSingleObject(handler_finish_semaphore_, INFINITE);
|
||||
bool status = handler_return_value_;
|
||||
|
||||
// Clean up.
|
||||
requesting_handler_ = NULL;
|
||||
requesting_thread_id_ = 0;
|
||||
exception_info_ = NULL;
|
||||
|
||||
LeaveCriticalSection(&handler_critical_section_);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
bool success = WriteMinidumpWithException(NULL);
|
||||
bool success = WriteMinidumpOnHandlerThread(NULL);
|
||||
UpdateNextID();
|
||||
return success;
|
||||
}
|
||||
|
@ -90,10 +171,12 @@ bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
|
|||
return handler.WriteMinidump();
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) {
|
||||
bool ExceptionHandler::WriteMinidumpWithException(DWORD requesting_thread_id,
|
||||
EXCEPTION_POINTERS *exinfo) {
|
||||
wchar_t dump_file_name[MAX_PATH];
|
||||
swprintf_s(dump_file_name, MAX_PATH, L"%s\\%s.dmp",
|
||||
dump_path_.c_str(), next_minidump_id_.c_str());
|
||||
WindowsStringUtils::safe_swprintf(dump_file_name, MAX_PATH, L"%s\\%s.dmp",
|
||||
dump_path_.c_str(),
|
||||
next_minidump_id_.c_str());
|
||||
|
||||
bool success = false;
|
||||
if (minidump_write_dump_) {
|
||||
|
@ -106,17 +189,38 @@ bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) {
|
|||
NULL);
|
||||
if (dump_file != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION except_info;
|
||||
except_info.ThreadId = GetCurrentThreadId();
|
||||
except_info.ThreadId = requesting_thread_id;
|
||||
except_info.ExceptionPointers = exinfo;
|
||||
except_info.ClientPointers = FALSE;
|
||||
|
||||
// Add an MDRawAirbagInfo stream to the minidump, to provide additional
|
||||
// information about the exception handler to the Airbag processor. The
|
||||
// information will help the processor determine which threads are
|
||||
// relevant. The Airbag processor does not require this information but
|
||||
// can function better with Airbag-generated dumps when it is present.
|
||||
// The native debugger is not harmed by the presence of this information.
|
||||
MDRawAirbagInfo airbag_info;
|
||||
airbag_info.validity = MD_AIRBAG_INFO_VALID_DUMP_THREAD_ID |
|
||||
MD_AIRBAG_INFO_VALID_REQUESTING_THREAD_ID;
|
||||
airbag_info.dump_thread_id = GetCurrentThreadId();
|
||||
airbag_info.requesting_thread_id = requesting_thread_id;
|
||||
|
||||
MINIDUMP_USER_STREAM airbag_info_stream;
|
||||
airbag_info_stream.Type = MD_AIRBAG_INFO_STREAM;
|
||||
airbag_info_stream.BufferSize = sizeof(airbag_info);
|
||||
airbag_info_stream.Buffer = &airbag_info;
|
||||
|
||||
MINIDUMP_USER_STREAM_INFORMATION user_streams;
|
||||
user_streams.UserStreamCount = 1;
|
||||
user_streams.UserStreamArray = &airbag_info_stream;
|
||||
|
||||
// The explicit comparison to TRUE avoids a warning (C4800).
|
||||
success = (minidump_write_dump_(GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
dump_file,
|
||||
MiniDumpNormal,
|
||||
&except_info,
|
||||
NULL,
|
||||
exinfo ? &except_info : NULL,
|
||||
&user_streams,
|
||||
NULL) == TRUE);
|
||||
|
||||
CloseHandle(dump_file);
|
||||
|
|
|
@ -63,8 +63,9 @@
|
|||
#include <DbgHelp.h>
|
||||
|
||||
#pragma warning( push )
|
||||
// disable exception handler warnings
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_airbag {
|
||||
|
@ -90,7 +91,7 @@ class ExceptionHandler {
|
|||
void *callback_context, bool install_handler);
|
||||
~ExceptionHandler();
|
||||
|
||||
// Get and set the minidump path
|
||||
// Get and set the minidump path.
|
||||
wstring dump_path() const { return dump_path_; }
|
||||
void set_dump_path(const wstring &dump_path) { dump_path_ = dump_path; }
|
||||
|
||||
|
@ -115,12 +116,30 @@ class ExceptionHandler {
|
|||
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
||||
|
||||
// This function does the actual writing of a minidump.
|
||||
bool WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo);
|
||||
// Runs the main loop for the exception handler thread.
|
||||
static DWORD WINAPI ExceptionHandlerThreadMain(void *lpParameter);
|
||||
|
||||
// Called when an unhandled exception occurs.
|
||||
// Called on the exception thread when an unhandled exception occurs.
|
||||
// Signals the exception handler thread to handle the exception.
|
||||
static LONG WINAPI HandleException(EXCEPTION_POINTERS *exinfo);
|
||||
|
||||
// This is called on the exception thread or on another thread that
|
||||
// the user wishes to produce a dump from. It calls
|
||||
// WriteMinidumpWithException on the handler thread, avoiding stack
|
||||
// overflows and inconsistent dumps due to writing the dump from
|
||||
// the exception thread. If the dump is requested as a result of an
|
||||
// exception, exinfo contains exception information, otherwise, it
|
||||
// is NULL.
|
||||
bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS *exinfo);
|
||||
|
||||
// This function does the actual writing of a minidump. It is called
|
||||
// on the handler thread. requesting_thread_id is the ID of the thread
|
||||
// that requested the dump. If the dump is requested as a result of
|
||||
// an exception, exinfo contains exception information, otherwise,
|
||||
// it is NULL.
|
||||
bool WriteMinidumpWithException(DWORD requesting_thread_id,
|
||||
EXCEPTION_POINTERS *exinfo);
|
||||
|
||||
// Generates a new ID and stores it in next_minidump_id_.
|
||||
void UpdateNextID();
|
||||
|
||||
|
@ -139,6 +158,41 @@ class ExceptionHandler {
|
|||
// the currently-installed ExceptionHandler, of which there can be only 1
|
||||
static ExceptionHandler *current_handler_;
|
||||
|
||||
// The exception handler thread, if one has been created.
|
||||
static HANDLE handler_thread_;
|
||||
|
||||
// The critical section enforcing the requirement that only one exception be
|
||||
// handled at a time.
|
||||
static CRITICAL_SECTION handler_critical_section_;
|
||||
|
||||
// Semaphores used to move exception handling between the exception thread
|
||||
// and the handler thread. handler_start_semaphore_ is signalled by the
|
||||
// exception thread to wake up the handler thread when an exception occurs.
|
||||
// handler_finish_semaphore_ is signalled by the handler thread to wake up
|
||||
// the exception thread when handling is complete.
|
||||
static HANDLE handler_start_semaphore_;
|
||||
static HANDLE handler_finish_semaphore_;
|
||||
|
||||
// The next 3 fields are static data passed from the requesting thread to
|
||||
// the handler thread.
|
||||
|
||||
// The ExceptionHandler through which a request to write a dump was routed.
|
||||
// This will be the same as current_handler_ for exceptions, but
|
||||
// user-requested dumps may be routed through any live ExceptionHandler.
|
||||
static ExceptionHandler *requesting_handler_;
|
||||
|
||||
// The thread ID of the thread requesting the dump (either the exception
|
||||
// thread or any other thread that called WriteMinidump directly).
|
||||
static DWORD requesting_thread_id_;
|
||||
|
||||
// The exception info passed to the exception handler on the exception
|
||||
// thread, if an exception occurred. NULL for user-requested dumps.
|
||||
static EXCEPTION_POINTERS *exception_info_;
|
||||
|
||||
// The return value of the handler, passed from the handler thread back to
|
||||
// the requesting thread.
|
||||
static bool handler_return_value_;
|
||||
|
||||
// disallow copy ctor and operator=
|
||||
explicit ExceptionHandler(const ExceptionHandler &);
|
||||
void operator=(const ExceptionHandler &);
|
||||
|
@ -147,4 +201,5 @@ class ExceptionHandler {
|
|||
} // namespace google_airbag
|
||||
|
||||
#pragma warning( pop )
|
||||
|
||||
#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
||||
|
|
|
@ -294,6 +294,14 @@
|
|||
RelativePath="..\..\..\common\windows\guid_string.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\google_airbag\common\minidump_format.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\string_utils-inl.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include "client/windows/sender/crash_report_sender.h"
|
||||
#include "common/windows/http_upload.h"
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
// wininet.lib.
|
||||
|
||||
#pragma warning( push )
|
||||
// disable exception handler warnings
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <map>
|
||||
|
@ -74,4 +74,5 @@ class CrashReportSender {
|
|||
} // namespace google_airbag
|
||||
|
||||
#pragma warning( pop )
|
||||
|
||||
#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
#include "common/windows/guid_string.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
@ -40,12 +42,13 @@ namespace google_airbag {
|
|||
// static
|
||||
wstring GUIDString::GUIDToWString(GUID *guid) {
|
||||
wchar_t guid_string[37];
|
||||
_snwprintf_s(guid_string, sizeof(guid_string) / sizeof(wchar_t), _TRUNCATE,
|
||||
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
guid->Data1, guid->Data2, guid->Data3,
|
||||
guid->Data4[0], guid->Data4[1], guid->Data4[2],
|
||||
guid->Data4[3], guid->Data4[4], guid->Data4[5],
|
||||
guid->Data4[6], guid->Data4[7]);
|
||||
WindowsStringUtils::safe_swprintf(
|
||||
guid_string, sizeof(guid_string) / sizeof(guid_string[0]),
|
||||
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
guid->Data1, guid->Data2, guid->Data3,
|
||||
guid->Data4[0], guid->Data4[1], guid->Data4[2],
|
||||
guid->Data4[3], guid->Data4[4], guid->Data4[5],
|
||||
guid->Data4[6], guid->Data4[7]);
|
||||
return wstring(guid_string);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,13 @@
|
|||
#include <Windows.h>
|
||||
#include <WinInet.h>
|
||||
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
#include "common/windows/http_upload.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
@ -164,7 +168,8 @@ wstring HTTPUpload::GenerateMultipartBoundary() {
|
|||
int r1 = rand();
|
||||
|
||||
wchar_t temp[kBoundaryLength];
|
||||
swprintf_s(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
|
||||
WindowsStringUtils::safe_swprintf(temp, kBoundaryLength, L"%s%08X%08X",
|
||||
kBoundaryPrefix, r0, r1);
|
||||
return wstring(temp);
|
||||
}
|
||||
|
||||
|
@ -230,8 +235,16 @@ bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
|||
// static
|
||||
void HTTPUpload::GetFileContents(const wstring &filename,
|
||||
vector<char> *contents) {
|
||||
// The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
|
||||
// wchar_t* filename, so use _wfopen directly in that case. For VC8 and
|
||||
// later, _wfopen has been deprecated in favor of _wfopen_s, which does
|
||||
// not exist in earlier versions, so let the ifstream open the file itself.
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
ifstream file;
|
||||
file.open(filename.c_str(), ios::binary);
|
||||
#else // _MSC_VER >= 1400
|
||||
ifstream file(_wfopen(filename.c_str(), L"rb"));
|
||||
#endif // _MSC_VER >= 1400
|
||||
if (file.is_open()) {
|
||||
file.seekg(0, ios::end);
|
||||
int length = file.tellg();
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#define COMMON_WINDOWS_HTTP_UPLOAD_H__
|
||||
|
||||
#pragma warning( push )
|
||||
// disable exception handler warnings
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <map>
|
||||
|
@ -104,4 +104,5 @@ class HTTPUpload {
|
|||
} // namespace google_airbag
|
||||
|
||||
#pragma warning( pop )
|
||||
|
||||
#endif // COMMON_WINDOWS_HTTP_UPLOAD_H__
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <dia2.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
#include "common/windows/pdb_source_line_writer.h"
|
||||
#include "common/windows/guid_string.h"
|
||||
|
||||
|
@ -77,6 +79,14 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
|
|||
return false;
|
||||
}
|
||||
break;
|
||||
case ANY_FILE:
|
||||
if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
|
||||
if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
|
||||
fprintf(stderr, "loadDataForPdb and loadDataFromExe failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown file format\n");
|
||||
return false;
|
||||
|
@ -153,7 +163,7 @@ bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) {
|
|||
stack_param_size = GetFunctionStackParamSize(function);
|
||||
}
|
||||
|
||||
fprintf(output_, "FUNC %x %llx %x %ws\n",
|
||||
fprintf(output_, "FUNC %x %" WIN_STRING_FORMAT_LL "x %x %ws\n",
|
||||
rva, length, stack_param_size, name);
|
||||
|
||||
CComPtr<IDiaEnumLineNumbers> lines;
|
||||
|
@ -379,6 +389,18 @@ bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::PrintPDBInfo() {
|
||||
wstring guid, filename;
|
||||
int age;
|
||||
if (!GetModuleInfo(&guid, &age, &filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(output_, "MODULE %ws %x %ws\n", guid.c_str(), age, filename.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// wcstol_positive_strict is sort of like wcstol, but much stricter. string
|
||||
// should be a buffer pointing to a null-terminated string containing only
|
||||
// decimal digits. If the entire string can be converted to an integer
|
||||
|
@ -441,6 +463,11 @@ bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
|
|||
// If a name comes from get_name because no undecorated form existed,
|
||||
// it's already formatted properly to be used as output. Don't do any
|
||||
// additional processing.
|
||||
//
|
||||
// MSVC7's DIA seems to not undecorate names in as many cases as MSVC8's.
|
||||
// This will result in calling get_name for some C++ symbols, so
|
||||
// all of the parameter and return type information may not be included in
|
||||
// the name string.
|
||||
} else {
|
||||
// C++ uses a bogus "void" argument for functions and methods that don't
|
||||
// take any parameters. Take it out of the undecorated name because it's
|
||||
|
@ -452,7 +479,8 @@ bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
|
|||
if (length >= replace_length) {
|
||||
wchar_t *name_end = *name + length - replace_length;
|
||||
if (wcscmp(name_end, replace_string) == 0) {
|
||||
wcscpy_s(name_end, replace_length, replacement_string);
|
||||
WindowsStringUtils::safe_wcscpy(name_end, replace_length,
|
||||
replacement_string);
|
||||
length = wcslen(*name);
|
||||
}
|
||||
}
|
||||
|
@ -481,13 +509,14 @@ bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
|
|||
|
||||
// Undecorate the name by moving it one character to the left in its
|
||||
// buffer, and terminating it where the last '@' had been.
|
||||
wcsncpy_s(*name, length, *name + 1, last_at - *name - 1);
|
||||
} else if (*name[0] == '_') {
|
||||
WindowsStringUtils::safe_wcsncpy(*name, length,
|
||||
*name + 1, last_at - *name - 1);
|
||||
} else if (*name[0] == '_') {
|
||||
// This symbol's name is encoded according to the cdecl rules. The
|
||||
// name doesn't end in a '@' character followed by a decimal positive
|
||||
// integer, so it's not a stdcall name. Strip off the leading
|
||||
// underscore.
|
||||
wcsncpy_s(*name, length, *name + 1, length - 1);
|
||||
WindowsStringUtils::safe_wcsncpy(*name, length, *name + 1, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -608,11 +637,12 @@ next_child:
|
|||
}
|
||||
|
||||
bool PDBSourceLineWriter::WriteMap(FILE *map_file) {
|
||||
bool ret = false;
|
||||
output_ = map_file;
|
||||
if (PrintSourceFiles() && PrintFunctions() && PrintFrameData()) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
bool ret = PrintPDBInfo() &&
|
||||
PrintSourceFiles() &&
|
||||
PrintFunctions() &&
|
||||
PrintFrameData();
|
||||
|
||||
output_ = NULL;
|
||||
return ret;
|
||||
|
@ -622,18 +652,47 @@ void PDBSourceLineWriter::Close() {
|
|||
session_.Release();
|
||||
}
|
||||
|
||||
wstring PDBSourceLineWriter::GetModuleGUID() {
|
||||
// static
|
||||
wstring PDBSourceLineWriter::GetBaseName(const wstring &filename) {
|
||||
wstring base_name(filename);
|
||||
size_t slash_pos = base_name.find_last_of(L"/\\");
|
||||
if (slash_pos != wstring::npos) {
|
||||
base_name.erase(0, slash_pos + 1);
|
||||
}
|
||||
return base_name;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::GetModuleInfo(wstring *guid, int *age,
|
||||
wstring *filename) {
|
||||
guid->clear();
|
||||
*age = 0;
|
||||
filename->clear();
|
||||
|
||||
CComPtr<IDiaSymbol> global;
|
||||
if (FAILED(session_->get_globalScope(&global))) {
|
||||
return L"";
|
||||
return false;
|
||||
}
|
||||
|
||||
GUID guid;
|
||||
if (FAILED(global->get_guid(&guid))) {
|
||||
return L"";
|
||||
GUID guid_number;
|
||||
if (FAILED(global->get_guid(&guid_number))) {
|
||||
return false;
|
||||
}
|
||||
*guid = GUIDString::GUIDToWString(&guid_number);
|
||||
|
||||
return GUIDString::GUIDToWString(&guid);
|
||||
// DWORD* and int* are not compatible. This is clean and avoids a cast.
|
||||
DWORD age_dword;
|
||||
if (FAILED(global->get_age(&age_dword))) {
|
||||
return false;
|
||||
}
|
||||
*age = age_dword;
|
||||
|
||||
CComBSTR filename_string;
|
||||
if (FAILED(global->get_symbolsFileName(&filename_string))) {
|
||||
return false;
|
||||
}
|
||||
*filename = GetBaseName(wstring(filename_string));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
||||
|
|
|
@ -50,6 +50,7 @@ class PDBSourceLineWriter {
|
|||
enum FileFormat {
|
||||
PDB_FILE, // a .pdb file containing debug symbols
|
||||
EXE_FILE, // a .exe or .dll file
|
||||
ANY_FILE // try PDB_FILE and then EXE_FILE
|
||||
};
|
||||
|
||||
explicit PDBSourceLineWriter();
|
||||
|
@ -73,9 +74,11 @@ class PDBSourceLineWriter {
|
|||
// Closes the current pdb file and its associated resources.
|
||||
void Close();
|
||||
|
||||
// Returns the GUID for the module, as a string,
|
||||
// e.g. "11111111-2222-3333-4444-555555555555".
|
||||
wstring GetModuleGUID();
|
||||
// Sets guid to the GUID for the module, as a string,
|
||||
// e.g. "11111111-2222-3333-4444-555555555555". age will be set to the
|
||||
// age of the pdb file, and filename will be set to the basename of the
|
||||
// PDB's file name. Returns true on success and false on failure.
|
||||
bool GetModuleInfo(wstring *guid, int *age, wstring *filename);
|
||||
|
||||
private:
|
||||
// Outputs the line/address pairs for each line in the enumerator.
|
||||
|
@ -102,6 +105,13 @@ class PDBSourceLineWriter {
|
|||
// correspond to code, returns true without outputting anything.
|
||||
bool PrintCodePublicSymbol(IDiaSymbol *symbol);
|
||||
|
||||
// Outputs a line identifying the PDB file that is being dumped, along with
|
||||
// its uuid and age.
|
||||
bool PrintPDBInfo();
|
||||
|
||||
// Returns the base name of a file, e.g. strips off the path.
|
||||
static wstring GetBaseName(const wstring &filename);
|
||||
|
||||
// Returns the function name for a symbol. If possible, the name is
|
||||
// undecorated. If the symbol's decorated form indicates the size of
|
||||
// parameters on the stack, this information is returned in stack_param_size.
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// string_utils-inl.h: Safer string manipulation on Windows, supporting
|
||||
// pre-MSVC8 environments.
|
||||
|
||||
#ifndef COMMON_WINDOWS_STRING_UTILS_INL_H__
|
||||
#define COMMON_WINDOWS_STRING_UTILS_INL_H__
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <wchar.h>
|
||||
|
||||
// The "ll" printf format size specifier corresponding to |long long| was
|
||||
// intrudced in MSVC8. Earlier versions did not provide this size specifier,
|
||||
// but "I64" can be used to print 64-bit types. Don't use "I64" where "ll"
|
||||
// is available, in the event of oddball systems where |long long| is not
|
||||
// 64 bits wide.
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
#define WIN_STRING_FORMAT_LL "ll"
|
||||
#else // MSC_VER >= 1400
|
||||
#define WIN_STRING_FORMAT_LL "I64"
|
||||
#endif // MSC_VER >= 1400
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
class WindowsStringUtils {
|
||||
public:
|
||||
// Equivalent to MSVC8's swprintf, which always 0-terminates buffer.
|
||||
static void safe_swprintf(wchar_t *buffer, size_t count,
|
||||
const wchar_t *format, ...);
|
||||
|
||||
// Roughly equivalent to MSVC8's wcscpy_s, except pre-MSVC8, this does
|
||||
// not fail if source is longer than destination_size. The destination
|
||||
// buffer is always 0-terminated.
|
||||
static void safe_wcscpy(wchar_t *destination, size_t destination_size,
|
||||
const wchar_t *source);
|
||||
|
||||
// Roughly equivalent to MSVC8's wcsncpy_s, except that _TRUNCATE cannot
|
||||
// be passed directly, and pre-MSVC8, this will not fail if source or count
|
||||
// are longer than destination_size. The destination buffer is always
|
||||
// 0-terminated.
|
||||
static void safe_wcsncpy(wchar_t *destination, size_t destination_size,
|
||||
const wchar_t *source, size_t count);
|
||||
|
||||
private:
|
||||
// Disallow instantiation and other object-based operations.
|
||||
WindowsStringUtils();
|
||||
WindowsStringUtils(const WindowsStringUtils&);
|
||||
~WindowsStringUtils();
|
||||
void operator=(const WindowsStringUtils&);
|
||||
};
|
||||
|
||||
// static
|
||||
inline void WindowsStringUtils::safe_swprintf(wchar_t *buffer, size_t count,
|
||||
const wchar_t *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vswprintf(buffer, count, format, args);
|
||||
|
||||
#if _MSC_VER < 1400 // MSVC 2005/8
|
||||
// Pre-MSVC 2005/8 doesn't 0-terminate the buffer if the formatted string
|
||||
// is larger than the buffer. Ensure that the string is 0-terminated.
|
||||
if (buffer && count)
|
||||
buffer[count - 1] = 0;
|
||||
#endif // _MSC_VER < 1400
|
||||
}
|
||||
|
||||
// static
|
||||
inline void WindowsStringUtils::safe_wcscpy(wchar_t *destination,
|
||||
size_t destination_size,
|
||||
const wchar_t *source) {
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
wcscpy_s(destination, destination_size, source);
|
||||
#else // _MSC_VER >= 1400
|
||||
// Pre-MSVC 2005/8 doesn't have wcscpy_s. Simulate it with wcsncpy.
|
||||
// wcsncpy doesn't 0-terminate the destination buffer if the source string
|
||||
// is longer than size. Ensure that the destination is 0-terminated.
|
||||
wcsncpy(destination, source, destination_size);
|
||||
if (destination && destination_size)
|
||||
destination[destination_size - 1] = 0;
|
||||
#endif // _MSC_VER >= 1400
|
||||
}
|
||||
|
||||
// static
|
||||
inline void WindowsStringUtils::safe_wcsncpy(wchar_t *destination,
|
||||
size_t destination_size,
|
||||
const wchar_t *source,
|
||||
size_t count) {
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
wcsncpy_s(destination, destination_size, source, count);
|
||||
#else // _MSC_VER >= 1400
|
||||
// Pre-MSVC 2005/8 doesn't have wcsncpy_s. Simulate it with wcsncpy.
|
||||
// wcsncpy doesn't 0-terminate the destination buffer if the source string
|
||||
// is longer than size. Ensure that the destination is 0-terminated.
|
||||
if (destination_size < count)
|
||||
count = destination_size;
|
||||
|
||||
wcsncpy(destination, source, count);
|
||||
if (destination && count)
|
||||
destination[count - 1] = 0;
|
||||
#endif // _MSC_VER >= 1400
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // COMMON_WINDOWS_STRING_UTILS_INL_H__
|
|
@ -47,6 +47,8 @@
|
|||
|
||||
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
|
||||
|
||||
namespace {
|
||||
|
||||
using google_airbag::AddressMap;
|
||||
using google_airbag::linked_ptr;
|
||||
|
||||
|
@ -185,6 +187,8 @@ static bool RunTests() {
|
|||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/stack_frame.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
using google_airbag::ContainedRangeMap;
|
||||
|
||||
|
||||
|
@ -248,6 +251,9 @@ static bool RunTests() {
|
|||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ typedef SSIZE_T ssize_t;
|
|||
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
#include "processor/minidump.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
|
||||
|
@ -462,13 +462,13 @@ void MinidumpContext::FreeContext() {
|
|||
|
||||
|
||||
bool MinidumpContext::CheckAgainstSystemInfo(u_int32_t context_cpu_type) {
|
||||
// It's OK if the minidump doesn't contain a SYSTEM_INFO_STREAM,
|
||||
// It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM,
|
||||
// as this function just implements a sanity check.
|
||||
MinidumpSystemInfo* system_info = minidump_->GetSystemInfo();
|
||||
if (!system_info)
|
||||
return true;
|
||||
|
||||
// If there is a SYSTEM_INFO_STREAM, it should contain valid system info.
|
||||
// If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info.
|
||||
const MDRawSystemInfo* raw_system_info = system_info->system_info();
|
||||
if (!raw_system_info)
|
||||
return false;
|
||||
|
@ -661,7 +661,8 @@ const u_int8_t* MinidumpMemoryRegion::GetMemory() {
|
|||
|
||||
|
||||
u_int64_t MinidumpMemoryRegion::GetBase() {
|
||||
return valid_ ? descriptor_->start_of_memory_range : (u_int64_t)-1;
|
||||
return valid_ ?
|
||||
descriptor_->start_of_memory_range : static_cast<u_int64_t>(-1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -829,8 +830,12 @@ MinidumpContext* MinidumpThread::GetContext() {
|
|||
}
|
||||
|
||||
|
||||
u_int32_t MinidumpThread::GetThreadID() {
|
||||
return valid_ ? thread_.thread_id : (u_int32_t)-1;
|
||||
bool MinidumpThread::GetThreadID(u_int32_t *thread_id) const {
|
||||
if (!thread_id || !valid_)
|
||||
return false;
|
||||
|
||||
*thread_id = thread_.thread_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -928,7 +933,10 @@ bool MinidumpThreadList::Read(u_int32_t expected_size) {
|
|||
if (!thread->Read())
|
||||
return false;
|
||||
|
||||
u_int32_t thread_id = thread->GetThreadID();
|
||||
u_int32_t thread_id;
|
||||
if (!thread->GetThreadID(&thread_id))
|
||||
return false;
|
||||
|
||||
if (GetThreadByID(thread_id)) {
|
||||
// Another thread with this ID is already in the list. Data error.
|
||||
return false;
|
||||
|
@ -1408,20 +1416,21 @@ void MinidumpModule::Print() {
|
|||
|
||||
MinidumpModuleList::MinidumpModuleList(Minidump* minidump)
|
||||
: MinidumpStream(minidump),
|
||||
range_map_(),
|
||||
range_map_(new RangeMap<u_int64_t, unsigned int>()),
|
||||
modules_(NULL),
|
||||
module_count_(0) {
|
||||
}
|
||||
|
||||
|
||||
MinidumpModuleList::~MinidumpModuleList() {
|
||||
delete range_map_;
|
||||
delete modules_;
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpModuleList::Read(u_int32_t expected_size) {
|
||||
// Invalidate cached data.
|
||||
range_map_.Clear();
|
||||
range_map_->Clear();
|
||||
delete modules_;
|
||||
modules_ = NULL;
|
||||
module_count_ = 0;
|
||||
|
@ -1460,7 +1469,7 @@ bool MinidumpModuleList::Read(u_int32_t expected_size) {
|
|||
if (base_address == (u_int64_t)-1)
|
||||
return false;
|
||||
|
||||
if (!range_map_.StoreRange(base_address, module_size, module_index))
|
||||
if (!range_map_->StoreRange(base_address, module_size, module_index))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1486,7 +1495,7 @@ MinidumpModule* MinidumpModuleList::GetModuleForAddress(u_int64_t address) {
|
|||
return NULL;
|
||||
|
||||
unsigned int module_index;
|
||||
if (!range_map_.RetrieveRange(address, &module_index, NULL, NULL))
|
||||
if (!range_map_->RetrieveRange(address, &module_index, NULL, NULL))
|
||||
return NULL;
|
||||
|
||||
return GetModuleAtIndex(module_index);
|
||||
|
@ -1518,7 +1527,7 @@ void MinidumpModuleList::Print() {
|
|||
|
||||
MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump)
|
||||
: MinidumpStream(minidump),
|
||||
range_map_(),
|
||||
range_map_(new RangeMap<u_int64_t, unsigned int>()),
|
||||
descriptors_(NULL),
|
||||
regions_(NULL),
|
||||
region_count_(0) {
|
||||
|
@ -1526,6 +1535,7 @@ MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump)
|
|||
|
||||
|
||||
MinidumpMemoryList::~MinidumpMemoryList() {
|
||||
delete range_map_;
|
||||
delete descriptors_;
|
||||
delete regions_;
|
||||
}
|
||||
|
@ -1537,7 +1547,7 @@ bool MinidumpMemoryList::Read(u_int32_t expected_size) {
|
|||
descriptors_ = NULL;
|
||||
delete regions_;
|
||||
regions_ = NULL;
|
||||
range_map_.Clear();
|
||||
range_map_->Clear();
|
||||
region_count_ = 0;
|
||||
|
||||
valid_ = false;
|
||||
|
@ -1587,7 +1597,7 @@ bool MinidumpMemoryList::Read(u_int32_t expected_size) {
|
|||
if (region_size == 0 || high_address < base_address)
|
||||
return false;
|
||||
|
||||
if (!range_map_.StoreRange(base_address, region_size, region_index))
|
||||
if (!range_map_->StoreRange(base_address, region_size, region_index))
|
||||
return false;
|
||||
|
||||
(*regions)[region_index].SetDescriptor(descriptor);
|
||||
|
@ -1617,7 +1627,7 @@ MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress(
|
|||
return NULL;
|
||||
|
||||
unsigned int region_index;
|
||||
if (!range_map_.RetrieveRange(address, ®ion_index, NULL, NULL))
|
||||
if (!range_map_->RetrieveRange(address, ®ion_index, NULL, NULL))
|
||||
return NULL;
|
||||
|
||||
return GetMemoryRegionAtIndex(region_index);
|
||||
|
@ -1708,8 +1718,12 @@ bool MinidumpException::Read(u_int32_t expected_size) {
|
|||
}
|
||||
|
||||
|
||||
u_int32_t MinidumpException::GetThreadID() {
|
||||
return valid_ ? exception_.thread_id : 0;
|
||||
bool MinidumpException::GetThreadID(u_int32_t *thread_id) const {
|
||||
if (!thread_id || !valid_)
|
||||
return false;
|
||||
|
||||
*thread_id = exception_.thread_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2006,6 +2020,85 @@ void MinidumpMiscInfo::Print() {
|
|||
printf(" processor_current_idle_state = 0x%x\n",
|
||||
misc_info_.processor_current_idle_state);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// MinidumpAirbagInfo
|
||||
//
|
||||
|
||||
|
||||
MinidumpAirbagInfo::MinidumpAirbagInfo(Minidump* minidump)
|
||||
: MinidumpStream(minidump),
|
||||
airbag_info_() {
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpAirbagInfo::Read(u_int32_t expected_size) {
|
||||
valid_ = false;
|
||||
|
||||
if (expected_size != sizeof(airbag_info_))
|
||||
return false;
|
||||
|
||||
if (!minidump_->ReadBytes(&airbag_info_, sizeof(airbag_info_)))
|
||||
return false;
|
||||
|
||||
if (minidump_->swap()) {
|
||||
Swap(&airbag_info_.validity);
|
||||
Swap(&airbag_info_.dump_thread_id);
|
||||
Swap(&airbag_info_.requesting_thread_id);
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpAirbagInfo::GetDumpThreadID(u_int32_t *thread_id) const {
|
||||
if (!thread_id || !valid_ ||
|
||||
!(airbag_info_.validity & MD_AIRBAG_INFO_VALID_DUMP_THREAD_ID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*thread_id = airbag_info_.dump_thread_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpAirbagInfo::GetRequestingThreadID(u_int32_t *thread_id)
|
||||
const {
|
||||
if (!thread_id || !valid_ ||
|
||||
!(airbag_info_.validity & MD_AIRBAG_INFO_VALID_REQUESTING_THREAD_ID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*thread_id = airbag_info_.requesting_thread_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void MinidumpAirbagInfo::Print() {
|
||||
if (!valid_)
|
||||
return;
|
||||
|
||||
printf("MDRawAirbagInfo\n");
|
||||
printf(" validity = 0x%x\n", airbag_info_.validity);
|
||||
|
||||
if (airbag_info_.validity & MD_AIRBAG_INFO_VALID_DUMP_THREAD_ID) {
|
||||
printf(" dump_thread_id = 0x%x\n", airbag_info_.dump_thread_id);
|
||||
} else {
|
||||
printf(" dump_thread_id = (invalid)\n");
|
||||
}
|
||||
|
||||
if (airbag_info_.validity & MD_AIRBAG_INFO_VALID_DUMP_THREAD_ID) {
|
||||
printf(" requesting_thread_id = 0x%x\n",
|
||||
airbag_info_.requesting_thread_id);
|
||||
} else {
|
||||
printf(" requesting_thread_id = (invalid)\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -2128,12 +2221,13 @@ bool Minidump::Read() {
|
|||
// type.
|
||||
unsigned int stream_type = directory_entry->stream_type;
|
||||
switch (stream_type) {
|
||||
case THREAD_LIST_STREAM:
|
||||
case MODULE_LIST_STREAM:
|
||||
case MEMORY_LIST_STREAM:
|
||||
case EXCEPTION_STREAM:
|
||||
case SYSTEM_INFO_STREAM:
|
||||
case MISC_INFO_STREAM: {
|
||||
case MD_THREAD_LIST_STREAM:
|
||||
case MD_MODULE_LIST_STREAM:
|
||||
case MD_MEMORY_LIST_STREAM:
|
||||
case MD_EXCEPTION_STREAM:
|
||||
case MD_SYSTEM_INFO_STREAM:
|
||||
case MD_MISC_INFO_STREAM:
|
||||
case MD_AIRBAG_INFO_STREAM: {
|
||||
if (stream_map->find(stream_type) != stream_map->end()) {
|
||||
// Another stream with this type was already found. A minidump
|
||||
// file should contain at most one of each of these stream types.
|
||||
|
@ -2194,6 +2288,12 @@ MinidumpMiscInfo* Minidump::GetMiscInfo() {
|
|||
}
|
||||
|
||||
|
||||
MinidumpAirbagInfo* Minidump::GetAirbagInfo() {
|
||||
MinidumpAirbagInfo* airbag_info;
|
||||
return GetStream(&airbag_info);
|
||||
}
|
||||
|
||||
|
||||
void Minidump::Print() {
|
||||
if (!valid_)
|
||||
return;
|
||||
|
@ -2233,7 +2333,7 @@ void Minidump::Print() {
|
|||
++iterator) {
|
||||
u_int32_t stream_type = iterator->first;
|
||||
MinidumpStreamInfo info = iterator->second;
|
||||
printf(" stream type %2d at index %d\n", stream_type, info.stream_index);
|
||||
printf(" stream type 0x%x at index %d\n", stream_type, info.stream_index);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
|
|
@ -32,81 +32,97 @@
|
|||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <string>
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
|
||||
#include "processor/minidump.h"
|
||||
namespace {
|
||||
|
||||
using google_airbag::Minidump;
|
||||
using google_airbag::MinidumpThreadList;
|
||||
using google_airbag::MinidumpModuleList;
|
||||
using google_airbag::MinidumpMemoryList;
|
||||
using google_airbag::MinidumpException;
|
||||
using google_airbag::MinidumpSystemInfo;
|
||||
using google_airbag::MinidumpMiscInfo;
|
||||
using google_airbag::MinidumpAirbagInfo;
|
||||
|
||||
using std::string;
|
||||
using namespace google_airbag;
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <file>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Minidump minidump(argv[1]);
|
||||
static bool PrintMinidumpDump(const char *minidump_file) {
|
||||
Minidump minidump(minidump_file);
|
||||
if (!minidump.Read()) {
|
||||
printf("minidump.Read() failed\n");
|
||||
exit(1);
|
||||
fprintf(stderr, "minidump.Read() failed\n");
|
||||
return false;
|
||||
}
|
||||
minidump.Print();
|
||||
|
||||
int error = 0;
|
||||
int errors = 0;
|
||||
|
||||
MinidumpThreadList* threadList = minidump.GetThreadList();
|
||||
if (!threadList) {
|
||||
error |= 1 << 2;
|
||||
MinidumpThreadList *thread_list = minidump.GetThreadList();
|
||||
if (!thread_list) {
|
||||
++errors;
|
||||
printf("minidump.GetThreadList() failed\n");
|
||||
} else {
|
||||
threadList->Print();
|
||||
thread_list->Print();
|
||||
}
|
||||
|
||||
MinidumpModuleList* moduleList = minidump.GetModuleList();
|
||||
if (!moduleList) {
|
||||
error |= 1 << 3;
|
||||
MinidumpModuleList *module_list = minidump.GetModuleList();
|
||||
if (!module_list) {
|
||||
++errors;
|
||||
printf("minidump.GetModuleList() failed\n");
|
||||
} else {
|
||||
moduleList->Print();
|
||||
module_list->Print();
|
||||
}
|
||||
|
||||
MinidumpMemoryList* memoryList = minidump.GetMemoryList();
|
||||
if (!memoryList) {
|
||||
error |= 1 << 4;
|
||||
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
|
||||
if (!memory_list) {
|
||||
++errors;
|
||||
printf("minidump.GetMemoryList() failed\n");
|
||||
} else {
|
||||
memoryList->Print();
|
||||
memory_list->Print();
|
||||
}
|
||||
|
||||
MinidumpException* exception = minidump.GetException();
|
||||
MinidumpException *exception = minidump.GetException();
|
||||
if (!exception) {
|
||||
error |= 1 << 5;
|
||||
// Exception info is optional, so don't treat this as an error.
|
||||
printf("minidump.GetException() failed\n");
|
||||
} else {
|
||||
exception->Print();
|
||||
}
|
||||
|
||||
MinidumpSystemInfo* systemInfo = minidump.GetSystemInfo();
|
||||
if (!systemInfo) {
|
||||
error |= 1 << 6;
|
||||
MinidumpSystemInfo *system_info = minidump.GetSystemInfo();
|
||||
if (!system_info) {
|
||||
++errors;
|
||||
printf("minidump.GetSystemInfo() failed\n");
|
||||
} else {
|
||||
systemInfo->Print();
|
||||
system_info->Print();
|
||||
}
|
||||
|
||||
MinidumpMiscInfo* miscInfo = minidump.GetMiscInfo();
|
||||
if (!miscInfo) {
|
||||
error |= 1 << 7;
|
||||
MinidumpMiscInfo *misc_info = minidump.GetMiscInfo();
|
||||
if (!misc_info) {
|
||||
++errors;
|
||||
printf("minidump.GetMiscInfo() failed\n");
|
||||
} else {
|
||||
miscInfo->Print();
|
||||
misc_info->Print();
|
||||
}
|
||||
|
||||
// Use return instead of exit to allow destructors to run.
|
||||
return(error);
|
||||
MinidumpAirbagInfo *airbag_info = minidump.GetAirbagInfo();
|
||||
if (!airbag_info) {
|
||||
// Airbag info is optional, so don't treat this as an error.
|
||||
printf("minidump.GetAirbagInfo() failed\n");
|
||||
} else {
|
||||
airbag_info->Print();
|
||||
}
|
||||
|
||||
return errors == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return PrintMinidumpDump(argv[1]) ? 0 : 1;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
testdata_dir=$srcdir/src/processor/testdata
|
||||
./src/processor/minidump_dump $testdata_dir/minidump1.dmp | \
|
||||
tr -s '\015' '\012' | \
|
||||
diff -u $testdata_dir/minidump1.out -
|
||||
./src/processor/minidump_dump $testdata_dir/minidump2.dmp | \
|
||||
tr -d '\015' | \
|
||||
diff -u $testdata_dir/minidump2.dump.out -
|
||||
exit $?
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
// (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 "google/minidump_processor.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/process_state.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "google_airbag/processor/minidump_processor.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "google_airbag/processor/process_state.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
|
||||
|
@ -54,11 +54,22 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||
process_state->cpu_ = GetCPUInfo(&dump, &process_state->cpu_info_);
|
||||
process_state->os_ = GetOSInfo(&dump, &process_state->os_version_);
|
||||
|
||||
u_int32_t exception_thread_id = 0;
|
||||
u_int32_t dump_thread_id = 0;
|
||||
bool has_dump_thread = false;
|
||||
u_int32_t requesting_thread_id = 0;
|
||||
bool has_requesting_thread = false;
|
||||
|
||||
MinidumpAirbagInfo *airbag_info = dump.GetAirbagInfo();
|
||||
if (airbag_info) {
|
||||
has_dump_thread = airbag_info->GetDumpThreadID(&dump_thread_id);
|
||||
has_requesting_thread =
|
||||
airbag_info->GetRequestingThreadID(&requesting_thread_id);
|
||||
}
|
||||
|
||||
MinidumpException *exception = dump.GetException();
|
||||
if (exception) {
|
||||
process_state->crashed_ = true;
|
||||
exception_thread_id = exception->GetThreadID();
|
||||
has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
|
||||
|
||||
process_state->crash_reason_ = GetCrashReason(
|
||||
&dump, &process_state->crash_address_);
|
||||
|
@ -69,7 +80,7 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool found_crash_thread = false;
|
||||
bool found_requesting_thread = false;
|
||||
unsigned int thread_count = threads->thread_count();
|
||||
for (unsigned int thread_index = 0;
|
||||
thread_index < thread_count;
|
||||
|
@ -79,15 +90,46 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (process_state->crashed_ &&
|
||||
thread->GetThreadID() == exception_thread_id) {
|
||||
if (found_crash_thread) {
|
||||
// There can't be more than one crash thread.
|
||||
u_int32_t thread_id;
|
||||
if (!thread->GetThreadID(&thread_id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// If this thread is the thread that produced the minidump, don't process
|
||||
// it. Because of the problems associated with a thread producing a
|
||||
// dump of itself (when both its context and its stack are in flux),
|
||||
// processing that stack wouldn't provide much useful data.
|
||||
if (has_dump_thread && thread_id == dump_thread_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MinidumpContext *context = thread->GetContext();
|
||||
|
||||
if (has_requesting_thread && thread_id == requesting_thread_id) {
|
||||
if (found_requesting_thread) {
|
||||
// There can't be more than one requesting thread.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
process_state->crash_thread_ = thread_index;
|
||||
found_crash_thread = true;
|
||||
// Use processed_state->threads_.size() instead of thread_index.
|
||||
// thread_index points to the thread index in the minidump, which
|
||||
// might be greater than the thread index in the threads vector if
|
||||
// any of the minidump's threads are skipped and not placed into the
|
||||
// processed threads vector. The thread vector's current size will
|
||||
// be the index of the current thread when it's pushed into the
|
||||
// vector.
|
||||
process_state->requesting_thread_ = process_state->threads_.size();
|
||||
|
||||
found_requesting_thread = true;
|
||||
|
||||
if (process_state->crashed_) {
|
||||
// Use the exception record's context for the crashed thread, instead
|
||||
// of the thread's own context. For the crashed thread, the thread's
|
||||
// own context is the state inside the exception handler. Using it
|
||||
// would not result in the expected stack trace from the time of the
|
||||
// crash.
|
||||
context = exception->GetContext();
|
||||
}
|
||||
}
|
||||
|
||||
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
|
||||
|
@ -96,7 +138,7 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||
}
|
||||
|
||||
scoped_ptr<Stackwalker> stackwalker(
|
||||
Stackwalker::StackwalkerForCPU(exception->GetContext(),
|
||||
Stackwalker::StackwalkerForCPU(context,
|
||||
thread_memory,
|
||||
dump.GetModuleList(),
|
||||
supplier_));
|
||||
|
@ -112,8 +154,8 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
|||
process_state->threads_.push_back(stack.release());
|
||||
}
|
||||
|
||||
// If the process crashed, there must be a crash thread.
|
||||
if (process_state->crashed_ && !found_crash_thread) {
|
||||
// If a requesting thread was indicated, it must be present.
|
||||
if (has_requesting_thread && !found_requesting_thread) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,19 +31,23 @@
|
|||
// corresponding symbol file, and checks the stack frames for correctness.
|
||||
|
||||
#include <string>
|
||||
#include "google/call_stack.h"
|
||||
#include "google/minidump_processor.h"
|
||||
#include "google/process_state.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "google/symbol_supplier.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "google_airbag/processor/minidump_processor.h"
|
||||
#include "google_airbag/processor/process_state.h"
|
||||
#include "google_airbag/processor/stack_frame.h"
|
||||
#include "google_airbag/processor/symbol_supplier.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using std::string;
|
||||
using google_airbag::CallStack;
|
||||
using google_airbag::MinidumpModule;
|
||||
using google_airbag::MinidumpProcessor;
|
||||
using google_airbag::ProcessState;
|
||||
using google_airbag::scoped_ptr;
|
||||
using google_airbag::SymbolSupplier;
|
||||
|
||||
#define ASSERT_TRUE(cond) \
|
||||
if (!(cond)) { \
|
||||
|
@ -53,8 +57,6 @@ using google_airbag::scoped_ptr;
|
|||
|
||||
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
class TestSymbolSupplier : public SymbolSupplier {
|
||||
public:
|
||||
virtual string GetSymbolFile(MinidumpModule *module);
|
||||
|
@ -62,17 +64,16 @@ class TestSymbolSupplier : public SymbolSupplier {
|
|||
|
||||
string TestSymbolSupplier::GetSymbolFile(MinidumpModule *module) {
|
||||
if (*(module->GetName()) == "c:\\test_app.exe") {
|
||||
// The funny-looking pathname is so that the symbol file can also be
|
||||
// reached by a SimpleSymbolSupplier.
|
||||
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||
"/src/processor/testdata/minidump2.sym";
|
||||
"/src/processor/testdata/symbols/"
|
||||
"test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1/test_app.sym";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
using google_airbag::TestSymbolSupplier;
|
||||
|
||||
static bool RunTests() {
|
||||
TestSymbolSupplier supplier;
|
||||
MinidumpProcessor processor(&supplier);
|
||||
|
@ -88,9 +89,9 @@ static bool RunTests() {
|
|||
ASSERT_EQ(state->os_version(), "5.1.2600 Service Pack 2");
|
||||
ASSERT_TRUE(state->crashed());
|
||||
ASSERT_EQ(state->crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
|
||||
ASSERT_EQ(state->crash_address(), 0);
|
||||
ASSERT_EQ(state->crash_address(), 0x45);
|
||||
ASSERT_EQ(state->threads()->size(), 1);
|
||||
ASSERT_EQ(state->crash_thread(), 0);
|
||||
ASSERT_EQ(state->requesting_thread(), 0);
|
||||
CallStack *stack = state->threads()->at(0);
|
||||
ASSERT_TRUE(stack);
|
||||
ASSERT_EQ(stack->frames()->size(), 4);
|
||||
|
@ -99,13 +100,13 @@ static bool RunTests() {
|
|||
ASSERT_EQ(stack->frames()->at(0)->module_name, "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(0)->function_name, "CrashFunction()");
|
||||
ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc");
|
||||
ASSERT_EQ(stack->frames()->at(0)->source_line, 65);
|
||||
ASSERT_EQ(stack->frames()->at(0)->source_line, 51);
|
||||
|
||||
ASSERT_EQ(stack->frames()->at(1)->module_base, 0x400000);
|
||||
ASSERT_EQ(stack->frames()->at(1)->module_name, "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
|
||||
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
|
||||
ASSERT_EQ(stack->frames()->at(1)->source_line, 70);
|
||||
ASSERT_EQ(stack->frames()->at(1)->source_line, 56);
|
||||
|
||||
// This comes from the CRT
|
||||
ASSERT_EQ(stack->frames()->at(2)->module_base, 0x400000);
|
||||
|
@ -126,6 +127,8 @@ static bool RunTests() {
|
|||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (!RunTests()) {
|
||||
return 1;
|
||||
|
|
|
@ -27,104 +27,212 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// minidump_stackwalk.cc: Print the stack of the exception thread from a
|
||||
// minidump.
|
||||
// minidump_stackwalk.cc: Process a minidump with MinidumpProcessor, printing
|
||||
// the results, including stack traces.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "google_airbag/processor/minidump_processor.h"
|
||||
#include "google_airbag/processor/process_state.h"
|
||||
#include "google_airbag/processor/stack_frame_cpu.h"
|
||||
#include "processor/pathname_stripper.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
#include "processor/simple_symbol_supplier.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using std::string;
|
||||
using google_airbag::CallStack;
|
||||
using google_airbag::MemoryRegion;
|
||||
using google_airbag::Minidump;
|
||||
using google_airbag::MinidumpContext;
|
||||
using google_airbag::MinidumpException;
|
||||
using google_airbag::MinidumpModuleList;
|
||||
using google_airbag::MinidumpThread;
|
||||
using google_airbag::MinidumpThreadList;
|
||||
using google_airbag::MinidumpModule;
|
||||
using google_airbag::MinidumpProcessor;
|
||||
using google_airbag::PathnameStripper;
|
||||
using google_airbag::ProcessState;
|
||||
using google_airbag::scoped_ptr;
|
||||
using google_airbag::SimpleSymbolSupplier;
|
||||
using google_airbag::StackFrame;
|
||||
using google_airbag::Stackwalker;
|
||||
using google_airbag::StackFramePPC;
|
||||
using google_airbag::StackFrameX86;
|
||||
|
||||
// PrintRegister prints a register's name and value to stdout. It will
|
||||
// print four registers on a line. For the first register in a set,
|
||||
// pass 0 for |sequence|. For registers in a set, pass the most recent
|
||||
// return value of PrintRegister. Note that PrintRegister will print a
|
||||
// newline before the first register (with |sequence| set to 0) is printed.
|
||||
// The caller is responsible for printing the final newline after a set
|
||||
// of registers is completely printed, regardless of the number of calls
|
||||
// to PrintRegister.
|
||||
static int PrintRegister(const char *name, u_int32_t value, int sequence) {
|
||||
if (sequence % 4 == 0) {
|
||||
printf("\n ");
|
||||
}
|
||||
printf(" %5s = 0x%08x", name, value);
|
||||
return ++sequence;
|
||||
}
|
||||
|
||||
// PrintStack prints the call stack in |stack| to stdout, in a reasonably
|
||||
// useful form. Module, function, and source file names are displayed if
|
||||
// they are available. The code offset to the base code address of the
|
||||
// source line, function, or module is printed, preferring them in that
|
||||
// order. If no source line, function, or module information is available,
|
||||
// an absolute code offset is printed.
|
||||
//
|
||||
// If |cpu| is a recognized CPU name, relevant register state for each stack
|
||||
// frame printed is also output, if available.
|
||||
static void PrintStack(const CallStack *stack, const string &cpu) {
|
||||
int frame_count = stack->frames()->size();
|
||||
for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
|
||||
const StackFrame *frame = stack->frames()->at(frame_index);
|
||||
printf("%2d ", frame_index);
|
||||
|
||||
if (!frame->module_name.empty()) {
|
||||
printf("%s", PathnameStripper::File(frame->module_name).c_str());
|
||||
if (!frame->function_name.empty()) {
|
||||
printf("!%s", frame->function_name.c_str());
|
||||
if (!frame->source_file_name.empty()) {
|
||||
string source_file = PathnameStripper::File(frame->source_file_name);
|
||||
printf(" [%s : %d + 0x%llx]", source_file.c_str(),
|
||||
frame->source_line,
|
||||
frame->instruction -
|
||||
frame->source_line_base);
|
||||
} else {
|
||||
printf(" + 0x%llx", frame->instruction - frame->function_base);
|
||||
}
|
||||
} else {
|
||||
printf(" + 0x%llx", frame->instruction - frame->module_base);
|
||||
}
|
||||
} else {
|
||||
printf("0x%llx", frame->instruction);
|
||||
}
|
||||
|
||||
int sequence = 0;
|
||||
if (cpu == "x86") {
|
||||
const StackFrameX86 *frame_x86 =
|
||||
reinterpret_cast<const StackFrameX86*>(frame);
|
||||
|
||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
|
||||
sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
|
||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
|
||||
sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
|
||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
|
||||
sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
|
||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
|
||||
sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
|
||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
|
||||
sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
|
||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
|
||||
sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
|
||||
if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
|
||||
sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
|
||||
sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
|
||||
sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
|
||||
sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
|
||||
}
|
||||
} else if (cpu == "ppc") {
|
||||
const StackFramePPC *frame_ppc =
|
||||
reinterpret_cast<const StackFramePPC*>(frame);
|
||||
|
||||
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
|
||||
sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
|
||||
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
|
||||
sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if
|
||||
// non-empty, is the base directory of a symbol storage area, laid out in
|
||||
// the format required by SimpleSymbolSupplier. If such a storage area
|
||||
// is specified, it is made available for use by the MinidumpProcessor.
|
||||
//
|
||||
// Returns the value of MinidumpProcessor::Process. If processing succeeds,
|
||||
// prints identifying OS and CPU information from the minidump, crash
|
||||
// information if the minidump was produced as a result of a crash, and
|
||||
// call stacks for each thread contained in the minidump. All information
|
||||
// is printed to stdout.
|
||||
static bool PrintMinidumpProcess(const string &minidump_file,
|
||||
const string &symbol_path) {
|
||||
scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
|
||||
if (!symbol_path.empty()) {
|
||||
// TODO(mmentovai): check existence of symbol_path if specified?
|
||||
symbol_supplier.reset(new SimpleSymbolSupplier(symbol_path));
|
||||
}
|
||||
|
||||
MinidumpProcessor minidump_processor(symbol_supplier.get());
|
||||
|
||||
// Process the minidump.
|
||||
scoped_ptr<ProcessState> process_state(
|
||||
minidump_processor.Process(minidump_file));
|
||||
if (!process_state.get()) {
|
||||
fprintf(stderr, "MinidumpProcessor::Process failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Print OS and CPU information.
|
||||
string cpu = process_state->cpu();
|
||||
string cpu_info = process_state->cpu_info();
|
||||
printf("Operating system: %s\n", process_state->os().c_str());
|
||||
printf(" %s\n", process_state->os_version().c_str());
|
||||
printf("CPU: %s\n", cpu.c_str());
|
||||
if (!cpu_info.empty()) {
|
||||
// This field is optional.
|
||||
printf(" %s\n", cpu_info.c_str());
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Print crash information.
|
||||
if (process_state->crashed()) {
|
||||
printf("Crash reason: %s\n", process_state->crash_reason().c_str());
|
||||
printf("Crash address: 0x%llx\n", process_state->crash_address());
|
||||
} else {
|
||||
printf("No crash\n");
|
||||
}
|
||||
|
||||
// If the thread that requested the dump is known, print it first.
|
||||
int requesting_thread = process_state->requesting_thread();
|
||||
if (requesting_thread != -1) {
|
||||
printf("\n");
|
||||
printf("Thread %d (%s)\n",
|
||||
requesting_thread,
|
||||
process_state->crashed() ? "crashed" :
|
||||
"requested dump, did not crash");
|
||||
PrintStack(process_state->threads()->at(requesting_thread), cpu);
|
||||
}
|
||||
|
||||
// Print all of the threads in the dump.
|
||||
int thread_count = process_state->threads()->size();
|
||||
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
|
||||
if (thread_index != requesting_thread) {
|
||||
// Don't print the crash thread again, it was already printed.
|
||||
printf("\n");
|
||||
printf("Thread %d\n", thread_index);
|
||||
PrintStack(process_state->threads()->at(thread_index), cpu);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <file>\n", argv[0]);
|
||||
exit(1);
|
||||
if (argc < 2 || argc > 3) {
|
||||
fprintf(stderr, "usage: %s <minidump-file> [symbol-path]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Minidump minidump(argv[1]);
|
||||
if (!minidump.Read()) {
|
||||
fprintf(stderr, "minidump.Read() failed\n");
|
||||
exit(1);
|
||||
const char *minidump_file = argv[1];
|
||||
const char *symbol_path = "";
|
||||
if (argc == 3) {
|
||||
symbol_path = argv[2];
|
||||
}
|
||||
|
||||
MinidumpException *exception = minidump.GetException();
|
||||
if (!exception) {
|
||||
fprintf(stderr, "minidump.GetException() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpThreadList *thread_list = minidump.GetThreadList();
|
||||
if (!thread_list) {
|
||||
fprintf(stderr, "minidump.GetThreadList() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpThread *exception_thread =
|
||||
thread_list->GetThreadByID(exception->GetThreadID());
|
||||
if (!exception_thread) {
|
||||
fprintf(stderr, "thread_list->GetThreadByID() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MemoryRegion *stack_memory = exception_thread->GetMemory();
|
||||
if (!stack_memory) {
|
||||
fprintf(stderr, "exception_thread->GetStackMemory() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpContext *context = exception->GetContext();
|
||||
if (!context) {
|
||||
fprintf(stderr, "exception->GetContext() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpModuleList *modules = minidump.GetModuleList();
|
||||
if (!modules) {
|
||||
fprintf(stderr, "minidump.GetModuleList() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
scoped_ptr<Stackwalker> stackwalker(
|
||||
Stackwalker::StackwalkerForCPU(context, stack_memory, modules, NULL));
|
||||
if (!stackwalker.get()) {
|
||||
fprintf(stderr, "Stackwalker::StackwalkerForCPU failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
scoped_ptr<CallStack> stack(stackwalker->Walk());
|
||||
|
||||
unsigned int index;
|
||||
for (index = 0; index < stack->frames()->size(); ++index) {
|
||||
StackFrame *frame = stack->frames()->at(index);
|
||||
printf("[%2d] instruction = 0x%08llx \"%s\" + 0x%08llx\n",
|
||||
index,
|
||||
frame->instruction,
|
||||
frame->module_base ? frame->module_name.c_str() : "0x0",
|
||||
frame->instruction - frame->module_base);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return PrintMinidumpProcess(minidump_file, symbol_path) ? 0 : 1;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
testdata_dir=$srcdir/src/processor/testdata
|
||||
./src/processor/minidump_stackwalk $testdata_dir/minidump1.dmp | \
|
||||
tr -s '\015' '\012' | \
|
||||
diff -u $testdata_dir/minidump1.stack.out -
|
||||
./src/processor/minidump_stackwalk $testdata_dir/minidump2.dmp \
|
||||
$testdata_dir/symbols | \
|
||||
tr -d '\015' | \
|
||||
diff -u $testdata_dir/minidump2.stackwalk.out -
|
||||
exit $?
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// pathname_stripper.cc: Manipulates pathnames into their component parts.
|
||||
//
|
||||
// See pathname_stripper.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include "processor/pathname_stripper.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
// static
|
||||
string PathnameStripper::File(const string &path) {
|
||||
string::size_type slash = path.rfind('/');
|
||||
string::size_type backslash = path.rfind('\\');
|
||||
|
||||
string::size_type file_start = 0;
|
||||
if (slash != string::npos &&
|
||||
(backslash == string::npos || slash > backslash)) {
|
||||
file_start = slash + 1;
|
||||
} else if (backslash != string::npos) {
|
||||
file_start = backslash + 1;
|
||||
}
|
||||
|
||||
return path.substr(file_start);
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// pathname_stripper.h: Manipulates pathnames into their component parts.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_PATHNAME_STRIPPER_H__
|
||||
#define PROCESSOR_PATHNAME_STRIPPER_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::string;
|
||||
|
||||
class PathnameStripper {
|
||||
public:
|
||||
// Given path, a pathname with components separated by slashes (/) or
|
||||
// backslashes (\), returns the trailing component, without any separator.
|
||||
// If path ends in a separator character, returns an empty string.
|
||||
static string File(const string &path);
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // PROCESSOR_PATHNAME_STRIPPER_H__
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) 2006, 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 "processor/pathname_stripper.h"
|
||||
|
||||
#define ASSERT_TRUE(condition) \
|
||||
if (!(condition)) { \
|
||||
fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
|
||||
|
||||
namespace {
|
||||
|
||||
using google_airbag::PathnameStripper;
|
||||
|
||||
static bool RunTests() {
|
||||
ASSERT_EQ(PathnameStripper::File("/dir/file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("\\dir\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("/dir\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("\\dir/file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir/file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir/\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir\\/file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir/"), "");
|
||||
ASSERT_EQ(PathnameStripper::File("dir\\"), "");
|
||||
ASSERT_EQ(PathnameStripper::File("dir/dir/"), "");
|
||||
ASSERT_EQ(PathnameStripper::File("dir\\dir\\"), "");
|
||||
ASSERT_EQ(PathnameStripper::File("dir1/dir2/file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir1\\dir2\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir1/dir2\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir1\\dir2/file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File(""), "");
|
||||
ASSERT_EQ(PathnameStripper::File("1"), "1");
|
||||
ASSERT_EQ(PathnameStripper::File("1/2"), "2");
|
||||
ASSERT_EQ(PathnameStripper::File("1\\2"), "2");
|
||||
ASSERT_EQ(PathnameStripper::File("/1/2"), "2");
|
||||
ASSERT_EQ(PathnameStripper::File("\\1\\2"), "2");
|
||||
ASSERT_EQ(PathnameStripper::File("dir//file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("dir\\\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("/dir//file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("\\dir\\\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("c:\\dir\\file"), "file");
|
||||
ASSERT_EQ(PathnameStripper::File("c:\\dir\\file.ext"), "file.ext");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
|
@ -26,7 +26,7 @@
|
|||
#include <sstream>
|
||||
|
||||
#include "processor/postfix_evaluator.h"
|
||||
#include "processor/memory_region.h"
|
||||
#include "google_airbag/processor/memory_region.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
|
|
@ -55,8 +55,6 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "processor/memory_region.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::map;
|
||||
|
|
|
@ -22,8 +22,11 @@
|
|||
|
||||
#include "processor/postfix_evaluator-inl.h"
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "processor/memory_region.h"
|
||||
#include "google_airbag/common/airbag_types.h"
|
||||
#include "google_airbag/processor/memory_region.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
using std::map;
|
||||
|
@ -84,7 +87,7 @@ struct EvaluateTestSet {
|
|||
};
|
||||
|
||||
|
||||
bool RunTests() {
|
||||
static bool RunTests() {
|
||||
// The first test set checks the basic operations and failure modes.
|
||||
PostfixEvaluator<unsigned int>::DictionaryType dictionary_0;
|
||||
const EvaluateTest evaluate_tests_0[] = {
|
||||
|
@ -274,6 +277,9 @@ bool RunTests() {
|
|||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include "google/process_state.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google_airbag/processor/process_state.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
#include "processor/scoped_ptr.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
using google_airbag::linked_ptr;
|
||||
using google_airbag::scoped_ptr;
|
||||
using google_airbag::RangeMap;
|
||||
|
@ -99,7 +102,7 @@ struct RangeTestSet {
|
|||
// StoreTest uses the data in a RangeTest and calls StoreRange on the
|
||||
// test RangeMap. It returns true if the expected result occurred, and
|
||||
// false if something else happened.
|
||||
bool StoreTest(TestMap *range_map, const RangeTest *range_test) {
|
||||
static bool StoreTest(TestMap *range_map, const RangeTest *range_test) {
|
||||
linked_ptr<CountedObject> object(new CountedObject(range_test->id));
|
||||
bool stored = range_map->StoreRange(range_test->address,
|
||||
range_test->size,
|
||||
|
@ -123,7 +126,7 @@ bool StoreTest(TestMap *range_map, const RangeTest *range_test) {
|
|||
// map entry at the specified range,) it returns true, otherwise, it returns
|
||||
// false. RetrieveTest will check the values around the base address and
|
||||
// the high address of a range to guard against off-by-one errors.
|
||||
bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
|
||||
static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
|
||||
for (unsigned int side = 0; side <= 1; ++side) {
|
||||
// When side == 0, check the low side (base address) of each range.
|
||||
// When side == 1, check the high side (base + size) of each range.
|
||||
|
@ -241,7 +244,7 @@ bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
|
|||
|
||||
|
||||
// RunTests runs a series of test sets.
|
||||
bool RunTests() {
|
||||
static bool RunTests() {
|
||||
// These tests will be run sequentially. The first set of tests exercises
|
||||
// most functions of RangeTest, and verifies all of the bounds-checking.
|
||||
const RangeTest range_tests_0[] = {
|
||||
|
@ -402,6 +405,10 @@ bool RunTests() {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// simple_symbol_supplier.cc: A simple SymbolSupplier implementation
|
||||
//
|
||||
// See simple_symbol_supplier.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include "processor/simple_symbol_supplier.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "processor/pathname_stripper.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
string SimpleSymbolSupplier::GetSymbolFileAtPath(MinidumpModule *module,
|
||||
const string &root_path) {
|
||||
// For now, only support modules that have GUIDs - which means
|
||||
// MDCVInfoPDB70.
|
||||
|
||||
if (!module)
|
||||
return "";
|
||||
|
||||
const MDCVInfoPDB70 *cv_record =
|
||||
reinterpret_cast<const MDCVInfoPDB70*>(module->GetCVRecord());
|
||||
if (!cv_record)
|
||||
return "";
|
||||
|
||||
if (cv_record->cv_signature != MD_CVINFOPDB70_SIGNATURE)
|
||||
return "";
|
||||
|
||||
// Start with the base path.
|
||||
string path = root_path;
|
||||
|
||||
// Append the pdb file name as a directory name.
|
||||
path.append("/");
|
||||
string pdb_file_name = PathnameStripper::File(
|
||||
reinterpret_cast<const char *>(cv_record->pdb_file_name));
|
||||
path.append(pdb_file_name);
|
||||
|
||||
// Append the uuid and age as a directory name.
|
||||
path.append("/");
|
||||
char uuid_age_string[43];
|
||||
snprintf(uuid_age_string, sizeof(uuid_age_string),
|
||||
"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X",
|
||||
cv_record->signature.data1, cv_record->signature.data2,
|
||||
cv_record->signature.data3,
|
||||
cv_record->signature.data4[0], cv_record->signature.data4[1],
|
||||
cv_record->signature.data4[2], cv_record->signature.data4[3],
|
||||
cv_record->signature.data4[4], cv_record->signature.data4[5],
|
||||
cv_record->signature.data4[6], cv_record->signature.data4[7],
|
||||
cv_record->age);
|
||||
path.append(uuid_age_string);
|
||||
|
||||
// Transform the pdb file name into one ending in .sym. If the existing
|
||||
// name ends in .pdb, strip the .pdb. Otherwise, add .sym to the non-.pdb
|
||||
// name.
|
||||
path.append("/");
|
||||
string pdb_file_extension = pdb_file_name.substr(pdb_file_name.size() - 4);
|
||||
transform(pdb_file_extension.begin(), pdb_file_extension.end(),
|
||||
pdb_file_extension.begin(), tolower);
|
||||
if (pdb_file_extension == ".pdb") {
|
||||
path.append(pdb_file_name.substr(0, pdb_file_name.size() - 4));
|
||||
} else {
|
||||
path.append(pdb_file_name);
|
||||
}
|
||||
path.append(".sym");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
// simple_symbol_supplier.h: A simple SymbolSupplier implementation
|
||||
//
|
||||
// SimpleSymbolSupplier is a straightforward implementation of SymbolSupplier
|
||||
// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is
|
||||
// created with a base directory, which is the root for all symbol files.
|
||||
// Each symbol file contained therien has a directory entry in the base
|
||||
// directory with a name identical to the corresponding pdb file. Within
|
||||
// each of these directories, there are subdirectories named for the uuid and
|
||||
// age of each pdb file. The uuid is presented in hexadecimal form, with
|
||||
// uppercase characters and no dashes. The age is appended to it in
|
||||
// hexadecimal form, without any separators. Within that subdirectory,
|
||||
// SimpleSymbolSupplier expects to find the symbol file, which is named
|
||||
// identically to the pdb file, but with a .sym extension. This sample
|
||||
// hierarchy is rooted at the "symbols" base directory:
|
||||
//
|
||||
// symbols
|
||||
// symbols/test_app.pdb
|
||||
// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1
|
||||
// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1/test_app.sym
|
||||
// symbols/kernel32.pdb
|
||||
// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542
|
||||
// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym
|
||||
//
|
||||
// In this case, the uuid of test_app.pdb is
|
||||
// 63fe4780-728d-4937-9b9d-7bb6460cb42a and its age is 1.
|
||||
//
|
||||
// This scheme was chosen to be roughly analogous to the way that
|
||||
// symbol files may be accessed from Microsoft Symbol Server. A hierarchy
|
||||
// used for Microsoft Symbol Server storage is usable as a hierarchy for
|
||||
// SimpleSymbolServer, provided that the pdb files are transformed to dumped
|
||||
// format using a tool such as dump_syms, and given a .sym extension.
|
||||
//
|
||||
// SimpleSymbolSupplier presently only supports symbol files that have
|
||||
// the MSVC 7.0 CodeView record format. See MDCVInfoPDB70 in
|
||||
// minidump_format.h.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
|
||||
#define PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "google_airbag/processor/symbol_supplier.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::string;
|
||||
|
||||
class MinidumpModule;
|
||||
|
||||
class SimpleSymbolSupplier : public SymbolSupplier {
|
||||
public:
|
||||
// Creates a new SimpleSymbolSupplier, using path as the root path where
|
||||
// symbols are stored.
|
||||
explicit SimpleSymbolSupplier(const string &path) : path_(path) {}
|
||||
|
||||
virtual ~SimpleSymbolSupplier() {}
|
||||
|
||||
// Returns the path to the symbol file for the given module. See the
|
||||
// description above.
|
||||
virtual string GetSymbolFile(MinidumpModule *module) {
|
||||
return GetSymbolFileAtPath(module, path_);
|
||||
}
|
||||
|
||||
protected:
|
||||
string GetSymbolFileAtPath(MinidumpModule *module, const string &root_path);
|
||||
|
||||
private:
|
||||
string path_;
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "google_airbag/common/airbag_types.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
|
|
@ -34,12 +34,12 @@
|
|||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#include "processor/stackwalker.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "google/symbol_supplier.h"
|
||||
#include "google_airbag/processor/stackwalker.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "google_airbag/processor/stack_frame.h"
|
||||
#include "google_airbag/processor/symbol_supplier.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/source_line_resolver.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
|
|
|
@ -35,9 +35,9 @@
|
|||
|
||||
|
||||
#include "processor/stackwalker_ppc.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame_cpu.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "google_airbag/processor/stack_frame_cpu.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
#define PROCESSOR_STACKWALKER_PPC_H__
|
||||
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "processor/stackwalker.h"
|
||||
#include "processor/minidump_format.h"
|
||||
#include "google_airbag/common/airbag_types.h"
|
||||
#include "google_airbag/common/minidump_format.h"
|
||||
#include "google_airbag/processor/stackwalker.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
|
|
@ -39,12 +39,12 @@
|
|||
|
||||
#include <cstdio>
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "google/stack_frame_cpu.h"
|
||||
#include "processor/memory_region.h"
|
||||
#include "processor/minidump_format.h"
|
||||
#include "google_airbag/common/airbag_types.h"
|
||||
#include "google_airbag/common/minidump_format.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/memory_region.h"
|
||||
#include "google_airbag/processor/stack_frame.h"
|
||||
#include "google_airbag/processor/stack_frame_cpu.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
using google_airbag::CallStack;
|
||||
|
|
|
@ -37,10 +37,10 @@
|
|||
#include "processor/postfix_evaluator-inl.h"
|
||||
|
||||
#include "processor/stackwalker_x86.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame_cpu.h"
|
||||
#include "google_airbag/processor/call_stack.h"
|
||||
#include "google_airbag/processor/minidump.h"
|
||||
#include "google_airbag/processor/stack_frame_cpu.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
@ -283,15 +283,15 @@ StackFrame* StackwalkerX86::GetCallerFrame(
|
|||
|
||||
// These are nonvolatile (callee-save) registers, and the program string
|
||||
// may have filled them in.
|
||||
if (dictionary_validity.find("$ebx") == dictionary_validity.end()) {
|
||||
if (dictionary_validity.find("$ebx") != dictionary_validity.end()) {
|
||||
frame->context.ebx = dictionary["$ebx"];
|
||||
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EBX;
|
||||
}
|
||||
if (dictionary_validity.find("$esi") == dictionary_validity.end()) {
|
||||
if (dictionary_validity.find("$esi") != dictionary_validity.end()) {
|
||||
frame->context.esi = dictionary["$esi"];
|
||||
frame->context_validity |= StackFrameX86::CONTEXT_VALID_ESI;
|
||||
}
|
||||
if (dictionary_validity.find("$edi") == dictionary_validity.end()) {
|
||||
if (dictionary_validity.find("$edi") != dictionary_validity.end()) {
|
||||
frame->context.edi = dictionary["$edi"];
|
||||
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI;
|
||||
}
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
#define PROCESSOR_STACKWALKER_X86_H__
|
||||
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "processor/stackwalker.h"
|
||||
#include "processor/minidump_format.h"
|
||||
#include "google_airbag/common/airbag_types.h"
|
||||
#include "google_airbag/common/minidump_format.h"
|
||||
#include "google_airbag/processor/stackwalker.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
|
Двоичные данные
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dmp
поставляемый
Двоичные данные
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dmp
поставляемый
Двоичный файл не отображается.
666
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dump.out
поставляемый
Executable file
666
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dump.out
поставляемый
Executable file
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
19
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out
поставляемый
Executable file
19
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out
поставляемый
Executable file
|
@ -0,0 +1,19 @@
|
|||
Operating system: Windows NT
|
||||
5.1.2600 Service Pack 2
|
||||
CPU: x86
|
||||
GenuineIntel family 6 model 13 stepping 8
|
||||
|
||||
Crash reason: EXCEPTION_ACCESS_VIOLATION
|
||||
Crash address: 0x45
|
||||
|
||||
Thread 0 (crashed)
|
||||
0 test_app.exe!CrashFunction() [test_app.cc : 51 + 0x3]
|
||||
eip = 0x0040208e esp = 0x0012feec ebp = 0x0012fef0 ebx = 0x7c80abc1
|
||||
esi = 0x00000002 edi = 0x00000a28 eax = 0x00000045 ecx = 0x0012fefc
|
||||
edx = 0x7c90eb94 efl = 0x00010246
|
||||
1 test_app.exe!main [test_app.cc : 56 + 0x4]
|
||||
eip = 0x004020df esp = 0x0012fef8 ebp = 0x0012ff70
|
||||
2 test_app.exe!__tmainCRTStartup [crt0.c : 318 + 0x11]
|
||||
eip = 0x0040395c esp = 0x0012ff78 ebp = 0x0012ffc0
|
||||
3 kernel32.dll!BaseProcessStart + 0x22
|
||||
eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0
|
|
@ -1,3 +1,4 @@
|
|||
MODULE 11111111-1111-1111-1111-111111111111 1 module1.pdb
|
||||
FILE 1 file1_1.cc
|
||||
FILE 2 file1_2.cc
|
||||
FILE 3 file1_3.cc
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
MODULE 22222222-2222-2222-2222-222222222222 2 module2.pdb
|
||||
FILE 1 file2_1.cc
|
||||
FILE 2 file2_2.cc
|
||||
FILE 3 file2_3.cc
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
MODULE 33333333-3333-3333-3333-333333333333 3 module3.pdb
|
||||
FILE 1 file1.cc
|
||||
FUNC 1000
|
||||
|
|
9730
toolkit/crashreporter/google-breakpad/src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym
поставляемый
Executable file
9730
toolkit/crashreporter/google-breakpad/src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym
поставляемый
Executable file
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -28,47 +28,32 @@
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// This file is used to generate minidump2.dmp and minidump2.sym.
|
||||
// cl /Zi /Fetest_app.exe test_app.cc dbghelp.lib
|
||||
// cl /Zi test_app.cc /Fetest_app.exe /I airbag/src \
|
||||
// airbag/src/client/windows/releasestaticcrt/exception_handler.lib \
|
||||
// ole32.lib
|
||||
// Then run test_app to generate a dump, and dump_syms to create the .sym file.
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <cstdio>
|
||||
|
||||
static LONG HandleException(EXCEPTION_POINTERS *exinfo) {
|
||||
HANDLE dump_file = CreateFile("dump.dmp",
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
#include "client/windows/handler/exception_handler.h"
|
||||
|
||||
MINIDUMP_EXCEPTION_INFORMATION except_info;
|
||||
except_info.ThreadId = GetCurrentThreadId();
|
||||
except_info.ExceptionPointers = exinfo;
|
||||
except_info.ClientPointers = false;
|
||||
|
||||
MiniDumpWriteDump(GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
dump_file,
|
||||
MiniDumpNormal,
|
||||
&except_info,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
CloseHandle(dump_file);
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
void callback(const std::wstring &id, void *context, bool succeeded) {
|
||||
if (succeeded) {
|
||||
printf("dump guid is %ws\n", id.c_str());
|
||||
} else {
|
||||
printf("dump failed\n");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void CrashFunction() {
|
||||
int *i = NULL;
|
||||
int *i = reinterpret_cast<int*>(0x45);
|
||||
*i = 5; // crash!
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
__try {
|
||||
CrashFunction();
|
||||
} __except(HandleException(GetExceptionInformation())) {
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
google_airbag::ExceptionHandler eh(L".", callback, NULL, true);
|
||||
CrashFunction();
|
||||
printf("did not crash?\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -39,20 +39,14 @@
|
|||
using std::wstring;
|
||||
using google_airbag::PDBSourceLineWriter;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int wmain(int argc, wchar_t **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s <pdb file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wchar_t filename[_MAX_PATH];
|
||||
if (mbstowcs_s(NULL, filename, argv[1], _MAX_PATH) == -1) {
|
||||
fprintf(stderr, "invalid multibyte character in %s\n", argv[1]);
|
||||
fprintf(stderr, "Usage: %ws <pdb file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
PDBSourceLineWriter writer;
|
||||
if (!writer.Open(wstring(filename), PDBSourceLineWriter::PDB_FILE)) {
|
||||
if (!writer.Open(wstring(argv[1]), PDBSourceLineWriter::ANY_FILE)) {
|
||||
fprintf(stderr, "Open failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -183,6 +183,10 @@
|
|||
RelativePath="..\..\..\common\windows\pdb_source_line_writer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\string_utils-inl.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
|
|
|
@ -29,10 +29,25 @@
|
|||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Release/dump_syms.exe testdata/dump_syms_regtest.pdb > testdata/dump_syms_regtest.new
|
||||
if diff -u testdata/dump_syms_regtest.new testdata/dump_syms_regtest.out >& testdata/dump_syms_regtest.diff; then
|
||||
Release/dump_syms.exe testdata/dump_syms_regtest.pdb | \
|
||||
tr -d '\015' > \
|
||||
testdata/dump_syms_regtest.new
|
||||
status=$?
|
||||
|
||||
if [ $status -ne 0 ] ; then
|
||||
echo "FAIL, dump_syms.exe failed"
|
||||
exit $status
|
||||
fi
|
||||
|
||||
diff -u testdata/dump_syms_regtest.new testdata/dump_syms_regtest.out > \
|
||||
testdata/dump_syms_regtest.diff
|
||||
status=$?
|
||||
|
||||
if [ $status -eq 0 ] ; then
|
||||
rm testdata/dump_syms_regtest.diff testdata/dump_syms_regtest.new
|
||||
echo "PASS"
|
||||
else
|
||||
echo "FAIL, see testdata/dump_syms_regtest.[new|diff]"
|
||||
fi
|
||||
|
||||
exit $status
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
#include "common/windows/http_upload.h"
|
||||
#include "common/windows/pdb_source_line_writer.h"
|
||||
|
||||
|
@ -55,6 +57,7 @@ using std::vector;
|
|||
using std::map;
|
||||
using google_airbag::HTTPUpload;
|
||||
using google_airbag::PDBSourceLineWriter;
|
||||
using google_airbag::WindowsStringUtils;
|
||||
|
||||
// Extracts the file version information for the given filename,
|
||||
// as a string, for example, "1.2.3.4". Returns true on success.
|
||||
|
@ -82,12 +85,13 @@ static bool GetFileVersionString(const wchar_t *filename, wstring *version) {
|
|||
wchar_t ver_string[24];
|
||||
VS_FIXEDFILEINFO *file_info =
|
||||
reinterpret_cast<VS_FIXEDFILEINFO*>(file_info_buffer);
|
||||
_snwprintf_s(ver_string, sizeof(ver_string) / sizeof(wchar_t), _TRUNCATE,
|
||||
L"%d.%d.%d.%d",
|
||||
file_info->dwFileVersionMS >> 16,
|
||||
file_info->dwFileVersionMS & 0xffff,
|
||||
file_info->dwFileVersionLS >> 16,
|
||||
file_info->dwFileVersionLS & 0xffff);
|
||||
WindowsStringUtils::safe_swprintf(
|
||||
ver_string, sizeof(ver_string) / sizeof(ver_string[0]),
|
||||
L"%d.%d.%d.%d",
|
||||
file_info->dwFileVersionMS >> 16,
|
||||
file_info->dwFileVersionMS & 0xffff,
|
||||
file_info->dwFileVersionLS >> 16,
|
||||
file_info->dwFileVersionLS & 0xffff);
|
||||
*version = ver_string;
|
||||
return true;
|
||||
}
|
||||
|
@ -95,11 +99,13 @@ static bool GetFileVersionString(const wchar_t *filename, wstring *version) {
|
|||
// Creates a new temporary file and writes the symbol data from the given
|
||||
// exe/dll file to it. Returns the path to the temp file in temp_file_path,
|
||||
// and the unique identifier (GUID) for the pdb in module_guid.
|
||||
static bool DumpSymbolsToTempFile(const wchar_t *exe_file,
|
||||
static bool DumpSymbolsToTempFile(const wchar_t *file,
|
||||
wstring *temp_file_path,
|
||||
wstring *module_guid) {
|
||||
wstring *module_guid,
|
||||
int *module_age,
|
||||
wstring *module_filename) {
|
||||
google_airbag::PDBSourceLineWriter writer;
|
||||
if (!writer.Open(exe_file, PDBSourceLineWriter::EXE_FILE)) {
|
||||
if (!writer.Open(file, PDBSourceLineWriter::ANY_FILE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -114,7 +120,13 @@ static bool DumpSymbolsToTempFile(const wchar_t *exe_file,
|
|||
}
|
||||
|
||||
FILE *temp_file = NULL;
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
if (_wfopen_s(&temp_file, temp_filename, L"w") != 0) {
|
||||
#else // _MSC_VER >= 1400
|
||||
// _wfopen_s was introduced in MSVC8. Use _wfopen for earlier environments.
|
||||
// Don't use it with MSVC8 and later, because it's deprecated.
|
||||
if (!(temp_file = _wfopen(temp_filename, L"w"))) {
|
||||
#endif // _MSC_VER >= 1400
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -126,37 +138,35 @@ static bool DumpSymbolsToTempFile(const wchar_t *exe_file,
|
|||
}
|
||||
|
||||
*temp_file_path = temp_filename;
|
||||
*module_guid = writer.GetModuleGUID();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the base name of a file, e.g. strips off the path.
|
||||
static wstring GetBaseName(const wstring &filename) {
|
||||
wstring base_name(filename);
|
||||
size_t slash_pos = base_name.find_last_of(L"/\\");
|
||||
if (slash_pos != string::npos) {
|
||||
base_name.erase(0, slash_pos + 1);
|
||||
}
|
||||
return base_name;
|
||||
return writer.GetModuleInfo(module_guid, module_age, module_filename);
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t *argv[]) {
|
||||
if (argc < 3) {
|
||||
wprintf(L"Usage: %s file.[exe|dll] <symbol upload URL>\n", argv[0]);
|
||||
wprintf(L"Usage: %s file.[pdb|exe|dll] <symbol upload URL>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
const wchar_t *module = argv[1], *url = argv[2];
|
||||
wstring module_basename = GetBaseName(module);
|
||||
|
||||
wstring symbol_file, module_guid;
|
||||
if (!DumpSymbolsToTempFile(module, &symbol_file, &module_guid)) {
|
||||
wstring symbol_file, module_guid, module_basename;
|
||||
int module_age;
|
||||
if (!DumpSymbolsToTempFile(module, &symbol_file,
|
||||
&module_guid, &module_age, &module_basename)) {
|
||||
fwprintf(stderr, L"Could not get symbol data from %s\n", module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wchar_t module_age_string[11];
|
||||
WindowsStringUtils::safe_swprintf(
|
||||
module_age_string,
|
||||
sizeof(module_age_string) / sizeof(module_age_string[0]),
|
||||
L"0x%x", module_age);
|
||||
|
||||
map<wstring, wstring> parameters;
|
||||
parameters[L"module"] = module_basename;
|
||||
parameters[L"guid"] = module_guid;
|
||||
parameters[L"age"] = module_age_string;
|
||||
|
||||
// Don't make a missing version a hard error. Issue a warning, and let the
|
||||
// server decide whether to reject files without versions.
|
||||
|
|
|
@ -188,6 +188,10 @@
|
|||
RelativePath="..\..\..\common\windows\pdb_source_line_writer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\string_utils-inl.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
|
|
Загрузка…
Ссылка в новой задаче