win: Save contents of TEBs allowing !teb and !gle to work in windbg

crashy_program's log looks something like this now:

0:000> .ecxr
eax=00000007 ebx=7f24e000 ecx=7f24d000 edx=00000000 esi=00497ec8 edi=00d39ca0
eip=00cf5d12 esp=001ffcd8 ebp=001ffcdc iopl=0         nv up ei ng nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010293
crashy_program+0x5d12:
00cf5d12 ??              ???
0:000> !teb
TEB at 7f24d000
    ExceptionList:        001ff548
    StackBase:            00200000
    StackLimit:           001fd000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7f24d000
    EnvironmentPointer:   00000000
    ClientId:             00003658 . 00004630
    RpcHandle:            00000000
    Tls Storage:          7f24d02c
    PEB Address:          7f24e000
    LastErrorValue:       2
    LastStatusValue:      c000000f
    Count Owned Locks:    0
    HardErrorMode:        0
0:000> !gle
LastErrorValue: (Win32) 0x2 (2) - The system cannot find the file specified.
LastStatusValue: (NTSTATUS) 0xc000000f - {File Not Found}  The file %hs does not exist.

R=mark@chromium.org
BUG=crashpad:46

Review URL: https://codereview.chromium.org/1364803004 .
This commit is contained in:
Scott Graham 2015-10-01 14:04:49 -07:00
parent 4df538f283
commit ecf3b37863
12 changed files with 143 additions and 16 deletions

View File

@ -12,15 +12,35 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "client/crashpad_client.h" #include <windows.h>
#include <winternl.h>
// ntstatus.h conflicts with windows.h so define this locally.
#ifndef STATUS_NO_SUCH_FILE
#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)
#endif
#include "base/logging.h" #include "base/logging.h"
#include "client/crashpad_client.h"
#include "tools/tool_support.h" #include "tools/tool_support.h"
namespace crashpad { namespace crashpad {
namespace { namespace {
ULONG RtlNtStatusToDosError(NTSTATUS status) {
static decltype(::RtlNtStatusToDosError)* rtl_nt_status_to_dos_error =
reinterpret_cast<decltype(::RtlNtStatusToDosError)*>(
GetProcAddress(LoadLibrary(L"ntdll.dll"), "RtlNtStatusToDosError"));
DCHECK(rtl_nt_status_to_dos_error);
return rtl_nt_status_to_dos_error(status);
}
void SomeCrashyFunction() { void SomeCrashyFunction() {
// SetLastError and NTSTATUS so that we have something to view in !gle in
// windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the
// LastStatusError of the TEB as a side-effect, and we'll be setting
// ERROR_FILE_NOT_FOUND for GetLastError().
SetLastError(RtlNtStatusToDosError(STATUS_NO_SUCH_FILE));
volatile int* foo = reinterpret_cast<volatile int*>(7); volatile int* foo = reinterpret_cast<volatile int*>(7);
*foo = 42; *foo = 42;
} }

View File

@ -150,6 +150,11 @@ void MinidumpThreadListWriter::InitializeFromSnapshot(
thread->InitializeFromSnapshot(thread_snapshot, thread_id_map); thread->InitializeFromSnapshot(thread_snapshot, thread_id_map);
AddThread(thread.Pass()); AddThread(thread.Pass());
} }
// Do this in a separate loop to keep the thread stacks earlier in the dump,
// and together.
for (const ThreadSnapshot* thread_snapshot : thread_snapshots)
memory_list_writer_->AddFromSnapshot(thread_snapshot->ExtraMemory());
} }
void MinidumpThreadListWriter::SetMemoryListWriter( void MinidumpThreadListWriter::SetMemoryListWriter(

View File

@ -18,6 +18,8 @@
#include <dbghelp.h> #include <dbghelp.h>
#include <sys/types.h> #include <sys/types.h>
#include <string>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/format_macros.h" #include "base/format_macros.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
@ -527,6 +529,9 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
uint64_t thread_ids[arraysize(expect_threads)] = {}; uint64_t thread_ids[arraysize(expect_threads)] = {};
uint8_t memory_values[arraysize(expect_threads)] = {}; uint8_t memory_values[arraysize(expect_threads)] = {};
uint32_t context_seeds[arraysize(expect_threads)] = {}; uint32_t context_seeds[arraysize(expect_threads)] = {};
MINIDUMP_MEMORY_DESCRIPTOR tebs[arraysize(expect_threads)] = {};
const size_t kTebSize = 1024;
expect_threads[0].ThreadId = 1; expect_threads[0].ThreadId = 1;
expect_threads[0].SuspendCount = 2; expect_threads[0].SuspendCount = 2;
@ -537,6 +542,8 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
expect_threads[0].ThreadContext.DataSize = sizeof(MinidumpContextType); expect_threads[0].ThreadContext.DataSize = sizeof(MinidumpContextType);
memory_values[0] = 'A'; memory_values[0] = 'A';
context_seeds[0] = 0x80000000; context_seeds[0] = 0x80000000;
tebs[0].StartOfMemoryRange = expect_threads[0].Teb;
tebs[0].Memory.DataSize = kTebSize;
// The thread at index 1 has no stack. // The thread at index 1 has no stack.
expect_threads[1].ThreadId = 11; expect_threads[1].ThreadId = 11;
@ -545,6 +552,8 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
expect_threads[1].Teb = 0xfedcba9876543210; expect_threads[1].Teb = 0xfedcba9876543210;
expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType); expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType);
context_seeds[1] = 0x40000001; context_seeds[1] = 0x40000001;
tebs[1].StartOfMemoryRange = expect_threads[1].Teb;
tebs[1].Memory.DataSize = kTebSize;
expect_threads[2].ThreadId = 21; expect_threads[2].ThreadId = 21;
expect_threads[2].SuspendCount = 22; expect_threads[2].SuspendCount = 22;
@ -555,6 +564,8 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType); expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType);
memory_values[2] = 'd'; memory_values[2] = 'd';
context_seeds[2] = 0x20000002; context_seeds[2] = 0x20000002;
tebs[2].StartOfMemoryRange = expect_threads[2].Teb;
tebs[2].Memory.DataSize = kTebSize;
if (thread_id_collision) { if (thread_id_collision) {
thread_ids[0] = 0x0123456700000001; thread_ids[0] = 0x0123456700000001;
@ -595,6 +606,12 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
Traits::InitializeCPUContext(thread_snapshot->MutableContext(), Traits::InitializeCPUContext(thread_snapshot->MutableContext(),
context_seeds[index]); context_seeds[index]);
auto teb_snapshot = make_scoped_ptr(new TestMemorySnapshot());
teb_snapshot->SetAddress(expect_threads[index].Teb);
teb_snapshot->SetSize(kTebSize);
teb_snapshot->SetValue(static_cast<char>('t' + index));
thread_snapshot->AddExtraMemory(teb_snapshot.Pass());
thread_snapshots.push_back(thread_snapshot); thread_snapshots.push_back(thread_snapshot);
} }
@ -617,7 +634,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
GetThreadListStream(string_file.string(), &thread_list, &memory_list)); GetThreadListStream(string_file.string(), &thread_list, &memory_list));
ASSERT_EQ(3u, thread_list->NumberOfThreads); ASSERT_EQ(3u, thread_list->NumberOfThreads);
ASSERT_EQ(2u, memory_list->NumberOfMemoryRanges); ASSERT_EQ(5u, memory_list->NumberOfMemoryRanges);
size_t memory_index = 0; size_t memory_index = 0;
for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) { for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) {
@ -643,7 +660,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
observed_stack, observed_stack,
string_file.string(), string_file.string(),
memory_values[index], memory_values[index],
index == thread_list->NumberOfThreads - 1)); false));
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
observed_stack, &memory_list->MemoryRanges[memory_index])); observed_stack, &memory_list->MemoryRanges[memory_index]));
@ -651,6 +668,18 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
++memory_index; ++memory_index;
} }
} }
for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) {
const MINIDUMP_MEMORY_DESCRIPTOR* memory =
&memory_list->MemoryRanges[memory_index];
ASSERT_NO_FATAL_FAILURE(
ExpectMinidumpMemoryDescriptor(&tebs[index], memory));
std::string expected_data(kTebSize, static_cast<char>('t' + index));
std::string observed_data(&string_file.string()[memory->Memory.Rva],
memory->Memory.DataSize);
EXPECT_EQ(expected_data, observed_data);
++memory_index;
}
} }
TEST(MinidumpThreadWriter, InitializeFromSnapshot_x86) { TEST(MinidumpThreadWriter, InitializeFromSnapshot_x86) {

View File

@ -98,8 +98,9 @@ class TestProcessSnapshot final : public ProcessSnapshot {
//! \brief Add a memory snapshot to be returned by ExtraMemory(). //! \brief Add a memory snapshot to be returned by ExtraMemory().
//! //!
//! \param[in] peb The memory snapshot that will be included in ExtraMemory(). //! \param[in] extra_memory The memory snapshot that will be included in
//! The TestProcessSnapshot object takes ownership of \a extra_memory. //! ExtraMemory(). The TestProcessSnapshot object takes ownership of \a
//! extra_memory.
void AddExtraMemory(scoped_ptr<MemorySnapshot> extra_memory) { void AddExtraMemory(scoped_ptr<MemorySnapshot> extra_memory) {
extra_memory_.push_back(extra_memory.release()); extra_memory_.push_back(extra_memory.release());
} }

View File

@ -55,5 +55,12 @@ uint64_t TestThreadSnapshot::ThreadSpecificDataAddress() const {
return thread_specific_data_address_; return thread_specific_data_address_;
} }
std::vector<const MemorySnapshot*> TestThreadSnapshot::ExtraMemory() const {
std::vector<const MemorySnapshot*> extra_memory;
for (const auto& em : extra_memory_)
extra_memory.push_back(em);
return extra_memory;
}
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

View File

@ -17,11 +17,14 @@
#include <stdint.h> #include <stdint.h>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "snapshot/cpu_context.h" #include "snapshot/cpu_context.h"
#include "snapshot/memory_snapshot.h" #include "snapshot/memory_snapshot.h"
#include "snapshot/thread_snapshot.h" #include "snapshot/thread_snapshot.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad { namespace crashpad {
namespace test { namespace test {
@ -61,6 +64,15 @@ class TestThreadSnapshot final : public ThreadSnapshot {
thread_specific_data_address_ = thread_specific_data_address; thread_specific_data_address_ = thread_specific_data_address;
} }
//! \brief Add a memory snapshot to be returned by ExtraMemory().
//!
//! \param[in] extra_memory The memory snapshot that will be included in
//! ExtraMemory(). The TestThreadSnapshot object takes ownership of \a
//! extra_memory.
void AddExtraMemory(scoped_ptr<MemorySnapshot> extra_memory) {
extra_memory_.push_back(extra_memory.release());
}
// ThreadSnapshot: // ThreadSnapshot:
const CPUContext* Context() const override; const CPUContext* Context() const override;
@ -69,6 +81,7 @@ class TestThreadSnapshot final : public ThreadSnapshot {
int SuspendCount() const override; int SuspendCount() const override;
int Priority() const override; int Priority() const override;
uint64_t ThreadSpecificDataAddress() const override; uint64_t ThreadSpecificDataAddress() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
private: private:
union { union {
@ -81,6 +94,7 @@ class TestThreadSnapshot final : public ThreadSnapshot {
int suspend_count_; int suspend_count_;
int priority_; int priority_;
uint64_t thread_specific_data_address_; uint64_t thread_specific_data_address_;
PointerVector<MemorySnapshot> extra_memory_;
DISALLOW_COPY_AND_ASSIGN(TestThreadSnapshot); DISALLOW_COPY_AND_ASSIGN(TestThreadSnapshot);
}; };

View File

@ -17,6 +17,8 @@
#include <stdint.h> #include <stdint.h>
#include <vector>
namespace crashpad { namespace crashpad {
struct CPUContext; struct CPUContext;
@ -62,6 +64,15 @@ class ThreadSnapshot {
//! \brief Returns the base address of a region used to store thread-specific //! \brief Returns the base address of a region used to store thread-specific
//! data. //! data.
virtual uint64_t ThreadSpecificDataAddress() const = 0; virtual uint64_t ThreadSpecificDataAddress() const = 0;
//! \brief Returns a vector of additional memory blocks that should be
//! included in a minidump.
//!
//! \return A vector of MemorySnapshot objects that will be included in the
//! crash dump. The caller does not take ownership of these objects, they
//! are scoped to the lifetime of the ThreadSnapshot object that they
//! were obtained from.
virtual std::vector<const MemorySnapshot*> ExtraMemory() const = 0;
}; };
} // namespace crashpad } // namespace crashpad

View File

@ -177,7 +177,8 @@ bool FillThreadContextAndSuspendCount(HANDLE thread_handle,
ProcessReaderWin::Thread::Thread() ProcessReaderWin::Thread::Thread()
: context(), : context(),
id(0), id(0),
teb(0), teb_address(0),
teb_size(0),
stack_region_address(0), stack_region_address(0),
stack_region_size(0), stack_region_size(0),
suspend_count(0), suspend_count(0),
@ -332,8 +333,9 @@ void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) {
// Read the TIB (Thread Information Block) which is the first element of the // Read the TIB (Thread Information Block) which is the first element of the
// TEB, for its stack fields. // TEB, for its stack fields.
process_types::NT_TIB<Traits> tib; process_types::NT_TIB<Traits> tib;
thread.teb = thread_basic_info.TebBaseAddress; thread.teb_address = thread_basic_info.TebBaseAddress;
if (ReadMemory(thread.teb, sizeof(tib), &tib)) { thread.teb_size = sizeof(process_types::TEB<Traits>);
if (ReadMemory(thread.teb_address, sizeof(tib), &tib)) {
WinVMAddress base = 0; WinVMAddress base = 0;
WinVMAddress limit = 0; WinVMAddress limit = 0;
// If we're reading a WOW64 process, then the TIB we just retrieved is the // If we're reading a WOW64 process, then the TIB we just retrieved is the
@ -341,8 +343,10 @@ void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) {
// https://msdn.microsoft.com/en-us/library/dn424783.aspx // https://msdn.microsoft.com/en-us/library/dn424783.aspx
if (is_64_reading_32) { if (is_64_reading_32) {
process_types::NT_TIB<process_types::internal::Traits32> tib32; process_types::NT_TIB<process_types::internal::Traits32> tib32;
thread.teb = tib.Wow64Teb; thread.teb_address = tib.Wow64Teb;
if (ReadMemory(thread.teb, sizeof(tib32), &tib32)) { thread.teb_size =
sizeof(process_types::TEB<process_types::internal::Traits32>);
if (ReadMemory(thread.teb_address, sizeof(tib32), &tib32)) {
base = tib32.StackBase; base = tib32.StackBase;
limit = tib32.StackLimit; limit = tib32.StackLimit;
} }

View File

@ -48,10 +48,11 @@ class ProcessReaderWin {
CONTEXT native; CONTEXT native;
#if defined(ARCH_CPU_64_BITS) #if defined(ARCH_CPU_64_BITS)
WOW64_CONTEXT wow64; WOW64_CONTEXT wow64;
#endif; #endif
} context; } context;
uint64_t id; uint64_t id;
WinVMAddress teb; WinVMAddress teb_address;
WinVMSize teb_size;
WinVMAddress stack_region_address; WinVMAddress stack_region_address;
WinVMSize stack_region_size; WinVMSize stack_region_size;
uint32_t suspend_count; uint32_t suspend_count;

View File

@ -14,6 +14,8 @@
#include "snapshot/win/thread_snapshot_win.h" #include "snapshot/win/thread_snapshot_win.h"
#include <vector>
#include "base/logging.h" #include "base/logging.h"
#include "snapshot/win/cpu_context_win.h" #include "snapshot/win/cpu_context_win.h"
#include "snapshot/win/process_reader_win.h" #include "snapshot/win/process_reader_win.h"
@ -22,7 +24,12 @@ namespace crashpad {
namespace internal { namespace internal {
ThreadSnapshotWin::ThreadSnapshotWin() ThreadSnapshotWin::ThreadSnapshotWin()
: ThreadSnapshot(), context_(), stack_(), thread_(), initialized_() { : ThreadSnapshot(),
context_(),
stack_(),
teb_(),
thread_(),
initialized_() {
} }
ThreadSnapshotWin::~ThreadSnapshotWin() { ThreadSnapshotWin::~ThreadSnapshotWin() {
@ -34,8 +41,11 @@ bool ThreadSnapshotWin::Initialize(
INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
thread_ = process_reader_thread; thread_ = process_reader_thread;
// TODO(scottmg): Ensure these regions are readable
// https://code.google.com/p/crashpad/issues/detail?id=59
stack_.Initialize( stack_.Initialize(
process_reader, thread_.stack_region_address, thread_.stack_region_size); process_reader, thread_.stack_region_address, thread_.stack_region_size);
teb_.Initialize(process_reader, thread_.teb_address, thread_.teb_size);
#if defined(ARCH_CPU_X86_64) #if defined(ARCH_CPU_X86_64)
if (process_reader->Is64Bit()) { if (process_reader->Is64Bit()) {
@ -84,7 +94,15 @@ int ThreadSnapshotWin::Priority() const {
uint64_t ThreadSnapshotWin::ThreadSpecificDataAddress() const { uint64_t ThreadSnapshotWin::ThreadSpecificDataAddress() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return thread_.teb; return thread_.teb_address;
}
std::vector<const MemorySnapshot*> ThreadSnapshotWin::ExtraMemory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(scottmg): Ensure this region is readable, and make sure we don't
// discard the entire dump if it isn't.
// https://code.google.com/p/crashpad/issues/detail?id=59
return std::vector<const MemorySnapshot*>(1, &teb_);
} }
} // namespace internal } // namespace internal

View File

@ -17,6 +17,8 @@
#include <stdint.h> #include <stdint.h>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "snapshot/cpu_context.h" #include "snapshot/cpu_context.h"
#include "snapshot/memory_snapshot.h" #include "snapshot/memory_snapshot.h"
@ -24,6 +26,7 @@
#include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/process_reader_win.h" #include "snapshot/win/process_reader_win.h"
#include "util/misc/initialization_state_dcheck.h" #include "util/misc/initialization_state_dcheck.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad { namespace crashpad {
@ -58,6 +61,7 @@ class ThreadSnapshotWin final : public ThreadSnapshot {
int SuspendCount() const override; int SuspendCount() const override;
int Priority() const override; int Priority() const override;
uint64_t ThreadSpecificDataAddress() const override; uint64_t ThreadSpecificDataAddress() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
private: private:
#if defined(ARCH_CPU_X86_FAMILY) #if defined(ARCH_CPU_X86_FAMILY)
@ -68,6 +72,7 @@ class ThreadSnapshotWin final : public ThreadSnapshot {
#endif #endif
CPUContext context_; CPUContext context_;
MemorySnapshotWin stack_; MemorySnapshotWin stack_;
internal::MemorySnapshotWin teb_;
ProcessReaderWin::Thread thread_; ProcessReaderWin::Thread thread_;
InitializationStateDcheck initialized_; InitializationStateDcheck initialized_;

View File

@ -309,12 +309,24 @@ struct CLIENT_ID {
}; };
// This is a partial definition of the TEB, as we do not currently use many // This is a partial definition of the TEB, as we do not currently use many
// fields of it. See http://www.nirsoft.net/kernel_struct/vista/TEB.html. // fields of it. See http://www.nirsoft.net/kernel_struct/vista/TEB.html, and
// the (arch-specific) definition of _TEB in winternl.h.
template <class Traits> template <class Traits>
struct TEB { struct TEB {
NT_TIB<Traits> NtTib; NT_TIB<Traits> NtTib;
typename Traits::Pointer EnvironmentPointer; typename Traits::Pointer ProcessEnvironmentBlock;
CLIENT_ID<Traits> ClientId; CLIENT_ID<Traits> ClientId;
// Not identical to Reserved2 in winternl's _TEB because we define ClientId.
typename Traits::Pointer RemainderOfReserved2[397];
BYTE Reserved3[1952];
typename Traits::Pointer TlsSlots[64];
BYTE Reserved4[8];
typename Traits::Pointer Reserved5[26];
typename Traits::Pointer ReservedForOle;
typename Traits::Pointer Reserved6[4];
typename Traits::Pointer TlsExpansionSlots;
}; };
// See https://msdn.microsoft.com/en-us/library/gg750724.aspx. // See https://msdn.microsoft.com/en-us/library/gg750724.aspx.