mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 09:17:57 +08:00
Add MinidumpThreadWriter, MinidumpThreadListWriter, and their test.
TEST=minidump_test MinidumpThreadWriter* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/637503006
This commit is contained in:
parent
8d36e3581c
commit
b8684a8a3c
@ -50,6 +50,8 @@
|
|||||||
'minidump_string_writer.h',
|
'minidump_string_writer.h',
|
||||||
'minidump_system_info_writer.cc',
|
'minidump_system_info_writer.cc',
|
||||||
'minidump_system_info_writer.h',
|
'minidump_system_info_writer.h',
|
||||||
|
'minidump_thread_writer.cc',
|
||||||
|
'minidump_thread_writer.h',
|
||||||
'minidump_writable.cc',
|
'minidump_writable.cc',
|
||||||
'minidump_writable.h',
|
'minidump_writable.h',
|
||||||
'minidump_writer_util.cc',
|
'minidump_writer_util.cc',
|
||||||
@ -83,6 +85,7 @@
|
|||||||
'minidump_system_info_writer_test.cc',
|
'minidump_system_info_writer_test.cc',
|
||||||
'minidump_test_util.cc',
|
'minidump_test_util.cc',
|
||||||
'minidump_test_util.h',
|
'minidump_test_util.h',
|
||||||
|
'minidump_thread_writer_test.cc',
|
||||||
'minidump_writable_test.cc',
|
'minidump_writable_test.cc',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -64,12 +64,9 @@ bool TestMinidumpMemoryWriter::WriteObject(FileWriterInterface* file_writer) {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpectMinidumpMemoryDescriptorAndContents(
|
void ExpectMinidumpMemoryDescriptor(
|
||||||
const MINIDUMP_MEMORY_DESCRIPTOR* expected,
|
const MINIDUMP_MEMORY_DESCRIPTOR* expected,
|
||||||
const MINIDUMP_MEMORY_DESCRIPTOR* observed,
|
const MINIDUMP_MEMORY_DESCRIPTOR* observed) {
|
||||||
const std::string& file_contents,
|
|
||||||
uint8_t value,
|
|
||||||
bool at_eof) {
|
|
||||||
EXPECT_EQ(expected->StartOfMemoryRange, observed->StartOfMemoryRange);
|
EXPECT_EQ(expected->StartOfMemoryRange, observed->StartOfMemoryRange);
|
||||||
EXPECT_EQ(expected->Memory.DataSize, observed->Memory.DataSize);
|
EXPECT_EQ(expected->Memory.DataSize, observed->Memory.DataSize);
|
||||||
if (expected->Memory.Rva != 0) {
|
if (expected->Memory.Rva != 0) {
|
||||||
@ -78,6 +75,16 @@ void ExpectMinidumpMemoryDescriptorAndContents(
|
|||||||
(expected->Memory.Rva + kMemoryAlignment - 1) & ~(kMemoryAlignment - 1),
|
(expected->Memory.Rva + kMemoryAlignment - 1) & ~(kMemoryAlignment - 1),
|
||||||
observed->Memory.Rva);
|
observed->Memory.Rva);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectMinidumpMemoryDescriptorAndContents(
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR* expected,
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR* observed,
|
||||||
|
const std::string& file_contents,
|
||||||
|
uint8_t value,
|
||||||
|
bool at_eof) {
|
||||||
|
ExpectMinidumpMemoryDescriptor(expected, observed);
|
||||||
|
|
||||||
if (at_eof) {
|
if (at_eof) {
|
||||||
EXPECT_EQ(file_contents.size(),
|
EXPECT_EQ(file_contents.size(),
|
||||||
observed->Memory.Rva + observed->Memory.DataSize);
|
observed->Memory.Rva + observed->Memory.DataSize);
|
||||||
|
@ -56,9 +56,7 @@ class TestMinidumpMemoryWriter final : public MinidumpMemoryWriter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//! \brief Verifies, via gtest assertions, that a MINIDUMP_MEMORY_DESCRIPTOR
|
//! \brief Verifies, via gtest assertions, that a MINIDUMP_MEMORY_DESCRIPTOR
|
||||||
//! structure contains expected values, and that the memory region it points
|
//! structure contains expected values.
|
||||||
//! to contains expected values assuming it was written by a
|
|
||||||
//! TestMinidumpMemoryWriter object.
|
|
||||||
//!
|
//!
|
||||||
//! In \a expected and \a observed,
|
//! In \a expected and \a observed,
|
||||||
//! MINIDUMP_MEMORY_DESCRIPTOR::StartOfMemoryRange and
|
//! MINIDUMP_MEMORY_DESCRIPTOR::StartOfMemoryRange and
|
||||||
@ -70,6 +68,21 @@ class TestMinidumpMemoryWriter final : public MinidumpMemoryWriter {
|
|||||||
//! expected values.
|
//! expected values.
|
||||||
//! \param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing
|
//! \param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing
|
||||||
//! observed values.
|
//! observed values.
|
||||||
|
void ExpectMinidumpMemoryDescriptor(const MINIDUMP_MEMORY_DESCRIPTOR* expected,
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR* observed);
|
||||||
|
|
||||||
|
//! \brief Verifies, via gtest assertions, that a MINIDUMP_MEMORY_DESCRIPTOR
|
||||||
|
//! structure contains expected values, and that the memory region it points
|
||||||
|
//! to contains expected values assuming it was written by a
|
||||||
|
//! TestMinidumpMemoryWriter object.
|
||||||
|
//!
|
||||||
|
//! \a expected and \a observed are compared by
|
||||||
|
//! ExpectMinidumpMemoryDescriptor().
|
||||||
|
//!
|
||||||
|
//! \param[in] expected A MINIDUMP_MEMORY_DESCRIPTOR structure containing
|
||||||
|
//! expected values.
|
||||||
|
//! \param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing
|
||||||
|
//! observed values.
|
||||||
//! \param[in] file_contents The contents of the minidump file in which \a
|
//! \param[in] file_contents The contents of the minidump file in which \a
|
||||||
//! observed was found. The memory region referenced by \a observed will be
|
//! observed was found. The memory region referenced by \a observed will be
|
||||||
//! read from this string.
|
//! read from this string.
|
||||||
|
179
minidump/minidump_thread_writer.cc
Normal file
179
minidump/minidump_thread_writer.cc
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "minidump/minidump_thread_writer.h"
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "minidump/minidump_context_writer.h"
|
||||||
|
#include "minidump/minidump_memory_writer.h"
|
||||||
|
#include "util/numeric/safe_assignment.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
MinidumpThreadWriter::MinidumpThreadWriter()
|
||||||
|
: MinidumpWritable(), thread_(), stack_(NULL), context_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
const MINIDUMP_THREAD* MinidumpThreadWriter::MinidumpThread() const {
|
||||||
|
DCHECK_EQ(state(), kStateWritable);
|
||||||
|
|
||||||
|
return &thread_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MinidumpThreadWriter::SetStack(MinidumpMemoryWriter* stack) {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
|
||||||
|
stack_ = stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MinidumpThreadWriter::SetContext(MinidumpContextWriter* context) {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
|
||||||
|
context_ = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpThreadWriter::Freeze() {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
CHECK(context_);
|
||||||
|
|
||||||
|
if (!MinidumpWritable::Freeze()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack_) {
|
||||||
|
stack_->RegisterMemoryDescriptor(&thread_.Stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
context_->RegisterLocationDescriptor(&thread_.ThreadContext);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MinidumpThreadWriter::SizeOfObject() {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
|
||||||
|
// This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is
|
||||||
|
// written by its parent as part of a MINIDUMP_THREAD_LIST, and its children
|
||||||
|
// are responsible for writing themselves.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<internal::MinidumpWritable*> MinidumpThreadWriter::Children() {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
DCHECK(context_);
|
||||||
|
|
||||||
|
std::vector<MinidumpWritable*> children;
|
||||||
|
if (stack_) {
|
||||||
|
children.push_back(stack_);
|
||||||
|
}
|
||||||
|
children.push_back(context_);
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpThreadWriter::WriteObject(FileWriterInterface* file_writer) {
|
||||||
|
DCHECK_EQ(state(), kStateWritable);
|
||||||
|
|
||||||
|
// This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is
|
||||||
|
// written by its parent as part of a MINIDUMP_THREAD_LIST, and its children
|
||||||
|
// are responsible for writing themselves.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpThreadListWriter::MinidumpThreadListWriter()
|
||||||
|
: MinidumpStreamWriter(),
|
||||||
|
thread_list_base_(),
|
||||||
|
threads_(),
|
||||||
|
memory_list_writer_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpThreadListWriter::~MinidumpThreadListWriter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void MinidumpThreadListWriter::SetMemoryListWriter(
|
||||||
|
MinidumpMemoryListWriter* memory_list_writer) {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
DCHECK(threads_.empty());
|
||||||
|
|
||||||
|
memory_list_writer_ = memory_list_writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MinidumpThreadListWriter::AddThread(MinidumpThreadWriter* thread) {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
|
||||||
|
threads_.push_back(thread);
|
||||||
|
|
||||||
|
if (memory_list_writer_) {
|
||||||
|
MinidumpMemoryWriter* stack = thread->Stack();
|
||||||
|
if (stack) {
|
||||||
|
memory_list_writer_->AddExtraMemory(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpThreadListWriter::Freeze() {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
|
||||||
|
if (!MinidumpStreamWriter::Freeze()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t thread_count = threads_.size();
|
||||||
|
if (!AssignIfInRange(&thread_list_base_.NumberOfThreads, thread_count)) {
|
||||||
|
LOG(ERROR) << "thread_count " << thread_count << " out of range";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MinidumpThreadListWriter::SizeOfObject() {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
|
||||||
|
return sizeof(thread_list_base_) + threads_.size() * sizeof(MINIDUMP_THREAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<internal::MinidumpWritable*> MinidumpThreadListWriter::Children() {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
|
||||||
|
std::vector<MinidumpWritable*> children;
|
||||||
|
for (MinidumpThreadWriter* thread : threads_) {
|
||||||
|
children.push_back(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpThreadListWriter::WriteObject(FileWriterInterface* file_writer) {
|
||||||
|
DCHECK_EQ(state(), kStateWritable);
|
||||||
|
|
||||||
|
WritableIoVec iov;
|
||||||
|
iov.iov_base = &thread_list_base_;
|
||||||
|
iov.iov_len = sizeof(thread_list_base_);
|
||||||
|
std::vector<WritableIoVec> iovecs(1, iov);
|
||||||
|
|
||||||
|
for (const MinidumpThreadWriter* thread : threads_) {
|
||||||
|
iov.iov_len = sizeof(MINIDUMP_THREAD);
|
||||||
|
iov.iov_base = thread->MinidumpThread();
|
||||||
|
iovecs.push_back(iov);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_writer->WriteIoVec(&iovecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpStreamType MinidumpThreadListWriter::StreamType() const {
|
||||||
|
return kMinidumpStreamTypeThreadList;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
185
minidump/minidump_thread_writer.h
Normal file
185
minidump/minidump_thread_writer.h
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_
|
||||||
|
#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_
|
||||||
|
|
||||||
|
#include <dbghelp.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/basictypes.h"
|
||||||
|
#include "minidump/minidump_stream_writer.h"
|
||||||
|
#include "minidump/minidump_writable.h"
|
||||||
|
#include "util/file/file_writer.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
class MinidumpContextWriter;
|
||||||
|
class MinidumpMemoryListWriter;
|
||||||
|
class MinidumpMemoryWriter;
|
||||||
|
|
||||||
|
//! \brief The writer for a MINIDUMP_THREAD object in a minidump file.
|
||||||
|
//!
|
||||||
|
//! Because MINIDUMP_THREAD objects only appear as elements of
|
||||||
|
//! MINIDUMP_THREAD_LIST objects, this class does not write any data on its own.
|
||||||
|
//! It makes its MINIDUMP_THREAD data available to its MinidumpThreadListWriter
|
||||||
|
//! parent, which writes it as part of a MINIDUMP_THREAD_LIST.
|
||||||
|
class MinidumpThreadWriter final : public internal::MinidumpWritable {
|
||||||
|
public:
|
||||||
|
MinidumpThreadWriter();
|
||||||
|
~MinidumpThreadWriter() {}
|
||||||
|
|
||||||
|
//! \brief Returns a MINIDUMP_THREAD referencing this object’s data.
|
||||||
|
//!
|
||||||
|
//! This method is expected to be called by a MinidumpThreadListWriter in
|
||||||
|
//! order to obtain a MINIDUMP_THREAD to include in its list.
|
||||||
|
//!
|
||||||
|
//! \note Valid in #kStateWritable.
|
||||||
|
const MINIDUMP_THREAD* MinidumpThread() const;
|
||||||
|
|
||||||
|
//! \brief Returns a MinidumpMemoryWriter that will write the memory region
|
||||||
|
//! corresponding to this object’s stack.
|
||||||
|
//!
|
||||||
|
//! If the thread does not have a stack, or its stack could not be determined,
|
||||||
|
//! this will return NULL.
|
||||||
|
//!
|
||||||
|
//! This method is provided so that MinidumpThreadListWriter can obtain thread
|
||||||
|
//! stack memory regions for the purposes of adding them to a
|
||||||
|
//! MinidumpMemoryListWriter (configured by calling
|
||||||
|
//! MinidumpThreadListWriter::SetMemoryListWriter()) by calling
|
||||||
|
//! MinidumpMemoryListWriter::AddExtraMemory().
|
||||||
|
//!
|
||||||
|
//! \note Valid in any state.
|
||||||
|
MinidumpMemoryWriter* Stack() const { return stack_; }
|
||||||
|
|
||||||
|
//! \brief Arranges for MINIDUMP_THREAD::Stack to point to the MINIDUMP_MEMORY
|
||||||
|
//! object to be written by \a stack.
|
||||||
|
//!
|
||||||
|
//! \a stack will become a child of this object in the overall tree of
|
||||||
|
//! internal::MinidumpWritable objects.
|
||||||
|
//!
|
||||||
|
//! \note Valid in #kStateMutable.
|
||||||
|
void SetStack(MinidumpMemoryWriter* stack);
|
||||||
|
|
||||||
|
//! \brief Arranges for MINIDUMP_THREAD::ThreadContext to point to the CPU
|
||||||
|
//! context to be written by \a context.
|
||||||
|
//!
|
||||||
|
//! A context is required in all MINIDUMP_THREAD objects.
|
||||||
|
//!
|
||||||
|
//! \a context will become a child of this object in the overall tree of
|
||||||
|
//! internal::MinidumpWritable objects.
|
||||||
|
//!
|
||||||
|
//! \note Valid in #kStateMutable.
|
||||||
|
void SetContext(MinidumpContextWriter* context);
|
||||||
|
|
||||||
|
//! \brief Sets MINIDUMP_THREAD::ThreadId.
|
||||||
|
void SetThreadID(uint32_t thread_id) { thread_.ThreadId = thread_id; }
|
||||||
|
|
||||||
|
//! \brief Sets MINIDUMP_THREAD::SuspendCount.
|
||||||
|
void SetSuspendCount(uint32_t suspend_count) {
|
||||||
|
thread_.SuspendCount = suspend_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \brief Sets MINIDUMP_THREAD::PriorityClass.
|
||||||
|
void SetPriorityClass(uint32_t priority_class) {
|
||||||
|
thread_.PriorityClass = priority_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \brief Sets MINIDUMP_THREAD::Priority.
|
||||||
|
void SetPriority(uint32_t priority) { thread_.Priority = priority; }
|
||||||
|
|
||||||
|
//! \brief Sets MINIDUMP_THREAD::Teb.
|
||||||
|
void SetTEB(uint64_t teb) { thread_.Teb = teb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// MinidumpWritable:
|
||||||
|
virtual bool Freeze() override;
|
||||||
|
virtual size_t SizeOfObject() override;
|
||||||
|
virtual std::vector<MinidumpWritable*> Children() override;
|
||||||
|
virtual bool WriteObject(FileWriterInterface* file_writer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MINIDUMP_THREAD thread_;
|
||||||
|
MinidumpMemoryWriter* stack_; // weak
|
||||||
|
MinidumpContextWriter* context_; // weak
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(MinidumpThreadWriter);
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief The writer for a MINIDUMP_THREAD_LIST stream in a minidump file,
|
||||||
|
//! containing a list of MINIDUMP_THREAD objects.
|
||||||
|
class MinidumpThreadListWriter final : public internal::MinidumpStreamWriter {
|
||||||
|
public:
|
||||||
|
MinidumpThreadListWriter();
|
||||||
|
~MinidumpThreadListWriter();
|
||||||
|
|
||||||
|
//! \brief Sets the MinidumpMemoryListWriter that each thread’s stack memory
|
||||||
|
//! region should be added to as extra memory.
|
||||||
|
//!
|
||||||
|
//! Each MINIDUMP_THREAD object can contain a reference to a
|
||||||
|
//! MinidumpMemoryWriter object that contains a snapshot of its stack memory.
|
||||||
|
//! In the overall tree of internal::MinidumpWritable objects, these
|
||||||
|
//! MinidumpMemoryWriter objects are considered children of their
|
||||||
|
//! MINIDUMP_THREAD, and are referenced by a MINIDUMP_MEMORY_DESCRIPTOR
|
||||||
|
//! contained in the MINIDUMP_THREAD. It is also possible for the same memory
|
||||||
|
//! regions to have MINIDUMP_MEMORY_DESCRIPTOR objects present in a
|
||||||
|
//! MINIDUMP_MEMORY_LIST stream. This is accomplished by calling this method,
|
||||||
|
//! which informs a MinidumpThreadListWriter that it should call
|
||||||
|
//! MinidumpMemoryListWriter::AddExtraMemory() for each extant thread stack
|
||||||
|
//! while the thread is being added in AddThread(). When this is done, the
|
||||||
|
//! MinidumpMemoryListWriter will contain a MINIDUMP_MEMORY_DESCRIPTOR
|
||||||
|
//! pointing to the thread’s stack memory in its MINIDUMP_MEMORY_LIST. Note
|
||||||
|
//! that the actual contents of the memory is only written once, as a child of
|
||||||
|
//! the MinidumpThreadWriter. The MINIDUMP_MEMORY_DESCRIPTOR objects in both
|
||||||
|
//! the MINIDUMP_THREAD and MINIDUMP_MEMORY_LIST will point to the same copy
|
||||||
|
//! of the memory’s contents.
|
||||||
|
//!
|
||||||
|
//! \note This method must be called before AddThread() is called. Threads
|
||||||
|
//! added by AddThread() prior to this method being called will not have
|
||||||
|
//! their stacks added to \a memory_list_writer as extra memory.
|
||||||
|
//! \note Valid in #kStateMutable.
|
||||||
|
void SetMemoryListWriter(MinidumpMemoryListWriter* memory_list_writer);
|
||||||
|
|
||||||
|
//! \brief Adds a MinidumpThreadWriter to the MINIDUMP_THREAD_LIST.
|
||||||
|
//!
|
||||||
|
//! \a thread will become a child of this object in the overall tree of
|
||||||
|
//! internal::MinidumpWritable objects.
|
||||||
|
//!
|
||||||
|
//! \note Valid in #kStateMutable.
|
||||||
|
void AddThread(MinidumpThreadWriter* thread);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// MinidumpWritable:
|
||||||
|
virtual bool Freeze() override;
|
||||||
|
virtual size_t SizeOfObject() override;
|
||||||
|
virtual std::vector<MinidumpWritable*> Children() override;
|
||||||
|
virtual bool WriteObject(FileWriterInterface* file_writer) override;
|
||||||
|
|
||||||
|
// MinidumpStreamWriter:
|
||||||
|
virtual MinidumpStreamType StreamType() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MINIDUMP_THREAD_LIST thread_list_base_;
|
||||||
|
std::vector<MinidumpThreadWriter*> threads_; // weak
|
||||||
|
MinidumpMemoryListWriter* memory_list_writer_; // weak
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(MinidumpThreadListWriter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_
|
490
minidump/minidump_thread_writer_test.cc
Normal file
490
minidump/minidump_thread_writer_test.cc
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "minidump/minidump_thread_writer.h"
|
||||||
|
|
||||||
|
#include <dbghelp.h>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "minidump/minidump_context_test_util.h"
|
||||||
|
#include "minidump/minidump_context_writer.h"
|
||||||
|
#include "minidump/minidump_memory_writer.h"
|
||||||
|
#include "minidump/minidump_memory_writer_test_util.h"
|
||||||
|
#include "minidump/minidump_file_writer.h"
|
||||||
|
#include "minidump/minidump_test_util.h"
|
||||||
|
#include "util/file/string_file_writer.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// This returns the MINIDUMP_THREAD_LIST stream in |thread_list|. If
|
||||||
|
// |memory_list| is non-NULL, a MINIDUMP_MEMORY_LIST stream is also expected in
|
||||||
|
// |file_contents|, and that stream will be returned in |memory_list|.
|
||||||
|
void GetThreadListStream(const std::string& file_contents,
|
||||||
|
const MINIDUMP_THREAD_LIST** thread_list,
|
||||||
|
const MINIDUMP_MEMORY_LIST** memory_list) {
|
||||||
|
const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
|
||||||
|
const uint32_t kExpectedStreams = memory_list ? 2 : 1;
|
||||||
|
const size_t kThreadListStreamOffset =
|
||||||
|
kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY);
|
||||||
|
const size_t kThreadsOffset =
|
||||||
|
kThreadListStreamOffset + sizeof(MINIDUMP_THREAD_LIST);
|
||||||
|
|
||||||
|
ASSERT_GE(file_contents.size(), kThreadsOffset);
|
||||||
|
|
||||||
|
const MINIDUMP_HEADER* header =
|
||||||
|
reinterpret_cast<const MINIDUMP_HEADER*>(&file_contents[0]);
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));
|
||||||
|
|
||||||
|
const MINIDUMP_DIRECTORY* directory =
|
||||||
|
reinterpret_cast<const MINIDUMP_DIRECTORY*>(
|
||||||
|
&file_contents[kDirectoryOffset]);
|
||||||
|
|
||||||
|
ASSERT_EQ(kMinidumpStreamTypeThreadList, directory[0].StreamType);
|
||||||
|
ASSERT_GE(directory[0].Location.DataSize, sizeof(MINIDUMP_THREAD_LIST));
|
||||||
|
ASSERT_EQ(kThreadListStreamOffset, directory[0].Location.Rva);
|
||||||
|
|
||||||
|
*thread_list = reinterpret_cast<const MINIDUMP_THREAD_LIST*>(
|
||||||
|
&file_contents[kThreadListStreamOffset]);
|
||||||
|
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_THREAD_LIST) +
|
||||||
|
(*thread_list)->NumberOfThreads * sizeof(MINIDUMP_THREAD),
|
||||||
|
directory[0].Location.DataSize);
|
||||||
|
|
||||||
|
if (memory_list) {
|
||||||
|
*memory_list = reinterpret_cast<const MINIDUMP_MEMORY_LIST*>(
|
||||||
|
&file_contents[directory[1].Location.Rva]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpThreadWriter, EmptyThreadList) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
MinidumpThreadListWriter thread_list_writer;
|
||||||
|
|
||||||
|
minidump_file_writer.AddStream(&thread_list_writer);
|
||||||
|
|
||||||
|
StringFileWriter file_writer;
|
||||||
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||||
|
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||||
|
sizeof(MINIDUMP_THREAD_LIST),
|
||||||
|
file_writer.string().size());
|
||||||
|
|
||||||
|
const MINIDUMP_THREAD_LIST* thread_list;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
GetThreadListStream(file_writer.string(), &thread_list, NULL));
|
||||||
|
|
||||||
|
EXPECT_EQ(0u, thread_list->NumberOfThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The MINIDUMP_THREADs |expected| and |observed| are compared against each
|
||||||
|
// other using gtest assertions. If |stack| is non-NULL, |observed| is expected
|
||||||
|
// to contain a populated MINIDUMP_MEMORY_DESCRIPTOR in its Stack field,
|
||||||
|
// otherwise, its Stack field is expected to be zeroed out. The memory
|
||||||
|
// descriptor will be placed in |stack|. |observed| must contain a populated
|
||||||
|
// ThreadContext field. The context will be recovered from |file_contents| and
|
||||||
|
// stored in |context_base|.
|
||||||
|
void ExpectThread(const MINIDUMP_THREAD* expected,
|
||||||
|
const MINIDUMP_THREAD* observed,
|
||||||
|
const std::string& file_contents,
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR** stack,
|
||||||
|
const void** context_base) {
|
||||||
|
EXPECT_EQ(expected->ThreadId, observed->ThreadId);
|
||||||
|
EXPECT_EQ(expected->SuspendCount, observed->SuspendCount);
|
||||||
|
EXPECT_EQ(expected->PriorityClass, observed->PriorityClass);
|
||||||
|
EXPECT_EQ(expected->Priority, observed->Priority);
|
||||||
|
EXPECT_EQ(expected->Teb, observed->Teb);
|
||||||
|
|
||||||
|
EXPECT_EQ(expected->Stack.StartOfMemoryRange,
|
||||||
|
observed->Stack.StartOfMemoryRange);
|
||||||
|
EXPECT_EQ(expected->Stack.Memory.DataSize, observed->Stack.Memory.DataSize);
|
||||||
|
if (stack) {
|
||||||
|
ASSERT_NE(0u, observed->Stack.Memory.DataSize);
|
||||||
|
ASSERT_NE(0u, observed->Stack.Memory.Rva);
|
||||||
|
ASSERT_GE(file_contents.size(),
|
||||||
|
observed->Stack.Memory.Rva + observed->Stack.Memory.DataSize);
|
||||||
|
*stack = &observed->Stack;
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(0u, observed->Stack.StartOfMemoryRange);
|
||||||
|
EXPECT_EQ(0u, observed->Stack.Memory.DataSize);
|
||||||
|
EXPECT_EQ(0u, observed->Stack.Memory.Rva);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(expected->ThreadContext.DataSize, observed->ThreadContext.DataSize);
|
||||||
|
ASSERT_NE(0u, observed->ThreadContext.DataSize);
|
||||||
|
ASSERT_NE(0u, observed->ThreadContext.Rva);
|
||||||
|
ASSERT_GE(file_contents.size(),
|
||||||
|
observed->ThreadContext.Rva + expected->ThreadContext.DataSize);
|
||||||
|
*context_base = &file_contents[observed->ThreadContext.Rva];
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpThreadWriter, OneThread_x86_NoStack) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
MinidumpThreadListWriter thread_list_writer;
|
||||||
|
|
||||||
|
const uint32_t kThreadID = 0x11111111;
|
||||||
|
const uint32_t kSuspendCount = 1;
|
||||||
|
const uint32_t kPriorityClass = 0x20;
|
||||||
|
const uint32_t kPriority = 10;
|
||||||
|
const uint64_t kTEB = 0x55555555;
|
||||||
|
const uint32_t kSeed = 123;
|
||||||
|
|
||||||
|
MinidumpThreadWriter thread_writer;
|
||||||
|
thread_writer.SetThreadID(kThreadID);
|
||||||
|
thread_writer.SetSuspendCount(kSuspendCount);
|
||||||
|
thread_writer.SetPriorityClass(kPriorityClass);
|
||||||
|
thread_writer.SetPriority(kPriority);
|
||||||
|
thread_writer.SetTEB(kTEB);
|
||||||
|
|
||||||
|
MinidumpContextX86Writer context_x86_writer;
|
||||||
|
InitializeMinidumpContextX86(context_x86_writer.context(), kSeed);
|
||||||
|
thread_writer.SetContext(&context_x86_writer);
|
||||||
|
|
||||||
|
thread_list_writer.AddThread(&thread_writer);
|
||||||
|
minidump_file_writer.AddStream(&thread_list_writer);
|
||||||
|
|
||||||
|
StringFileWriter file_writer;
|
||||||
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||||
|
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||||
|
sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) +
|
||||||
|
1 * sizeof(MinidumpContextX86),
|
||||||
|
file_writer.string().size());
|
||||||
|
|
||||||
|
const MINIDUMP_THREAD_LIST* thread_list;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
GetThreadListStream(file_writer.string(), &thread_list, NULL));
|
||||||
|
|
||||||
|
EXPECT_EQ(1u, thread_list->NumberOfThreads);
|
||||||
|
|
||||||
|
MINIDUMP_THREAD expected = {};
|
||||||
|
expected.ThreadId = kThreadID;
|
||||||
|
expected.SuspendCount = kSuspendCount;
|
||||||
|
expected.PriorityClass = kPriorityClass;
|
||||||
|
expected.Priority = kPriority;
|
||||||
|
expected.Teb = kTEB;
|
||||||
|
expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
|
||||||
|
|
||||||
|
const MinidumpContextX86* observed_context;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectThread(&expected,
|
||||||
|
&thread_list->Threads[0],
|
||||||
|
file_writer.string(),
|
||||||
|
NULL,
|
||||||
|
reinterpret_cast<const void**>(&observed_context)));
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed, observed_context));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpThreadWriter, OneThread_AMD64_Stack) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
MinidumpThreadListWriter thread_list_writer;
|
||||||
|
|
||||||
|
const uint32_t kThreadID = 0x22222222;
|
||||||
|
const uint32_t kSuspendCount = 2;
|
||||||
|
const uint32_t kPriorityClass = 0x30;
|
||||||
|
const uint32_t kPriority = 20;
|
||||||
|
const uint64_t kTEB = 0x5555555555555555;
|
||||||
|
const uint64_t kMemoryBase = 0x765432100000;
|
||||||
|
const size_t kMemorySize = 32;
|
||||||
|
const uint8_t kMemoryValue = 99;
|
||||||
|
const uint32_t kSeed = 456;
|
||||||
|
|
||||||
|
MinidumpThreadWriter thread_writer;
|
||||||
|
thread_writer.SetThreadID(kThreadID);
|
||||||
|
thread_writer.SetSuspendCount(kSuspendCount);
|
||||||
|
thread_writer.SetPriorityClass(kPriorityClass);
|
||||||
|
thread_writer.SetPriority(kPriority);
|
||||||
|
thread_writer.SetTEB(kTEB);
|
||||||
|
|
||||||
|
TestMinidumpMemoryWriter memory_writer(
|
||||||
|
kMemoryBase, kMemorySize, kMemoryValue);
|
||||||
|
thread_writer.SetStack(&memory_writer);
|
||||||
|
|
||||||
|
MinidumpContextAMD64Writer context_amd64_writer;
|
||||||
|
InitializeMinidumpContextAMD64(context_amd64_writer.context(), kSeed);
|
||||||
|
thread_writer.SetContext(&context_amd64_writer);
|
||||||
|
|
||||||
|
thread_list_writer.AddThread(&thread_writer);
|
||||||
|
minidump_file_writer.AddStream(&thread_list_writer);
|
||||||
|
|
||||||
|
StringFileWriter file_writer;
|
||||||
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||||
|
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||||
|
sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) +
|
||||||
|
1 * sizeof(MinidumpContextAMD64) + kMemorySize,
|
||||||
|
file_writer.string().size());
|
||||||
|
|
||||||
|
const MINIDUMP_THREAD_LIST* thread_list;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
GetThreadListStream(file_writer.string(), &thread_list, NULL));
|
||||||
|
|
||||||
|
EXPECT_EQ(1u, thread_list->NumberOfThreads);
|
||||||
|
|
||||||
|
MINIDUMP_THREAD expected = {};
|
||||||
|
expected.ThreadId = kThreadID;
|
||||||
|
expected.SuspendCount = kSuspendCount;
|
||||||
|
expected.PriorityClass = kPriorityClass;
|
||||||
|
expected.Priority = kPriority;
|
||||||
|
expected.Teb = kTEB;
|
||||||
|
expected.Stack.StartOfMemoryRange = kMemoryBase;
|
||||||
|
expected.Stack.Memory.DataSize = kMemorySize;
|
||||||
|
expected.ThreadContext.DataSize = sizeof(MinidumpContextAMD64);
|
||||||
|
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack;
|
||||||
|
const MinidumpContextAMD64* observed_context;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectThread(&expected,
|
||||||
|
&thread_list->Threads[0],
|
||||||
|
file_writer.string(),
|
||||||
|
&observed_stack,
|
||||||
|
reinterpret_cast<const void**>(&observed_context)));
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
|
||||||
|
observed_stack,
|
||||||
|
file_writer.string(),
|
||||||
|
kMemoryValue,
|
||||||
|
true));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextAMD64(kSeed, observed_context));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
MinidumpThreadListWriter thread_list_writer;
|
||||||
|
MinidumpMemoryListWriter memory_list_writer;
|
||||||
|
thread_list_writer.SetMemoryListWriter(&memory_list_writer);
|
||||||
|
|
||||||
|
const uint32_t kThreadID0 = 1111111;
|
||||||
|
const uint32_t kSuspendCount0 = 111111;
|
||||||
|
const uint32_t kPriorityClass0 = 11111;
|
||||||
|
const uint32_t kPriority0 = 1111;
|
||||||
|
const uint64_t kTEB0 = 111;
|
||||||
|
const uint64_t kMemoryBase0 = 0x1110;
|
||||||
|
const size_t kMemorySize0 = 16;
|
||||||
|
const uint8_t kMemoryValue0 = 11;
|
||||||
|
const uint32_t kSeed0 = 1;
|
||||||
|
|
||||||
|
MinidumpThreadWriter thread_writer_0;
|
||||||
|
thread_writer_0.SetThreadID(kThreadID0);
|
||||||
|
thread_writer_0.SetSuspendCount(kSuspendCount0);
|
||||||
|
thread_writer_0.SetPriorityClass(kPriorityClass0);
|
||||||
|
thread_writer_0.SetPriority(kPriority0);
|
||||||
|
thread_writer_0.SetTEB(kTEB0);
|
||||||
|
|
||||||
|
TestMinidumpMemoryWriter memory_writer_0(
|
||||||
|
kMemoryBase0, kMemorySize0, kMemoryValue0);
|
||||||
|
thread_writer_0.SetStack(&memory_writer_0);
|
||||||
|
|
||||||
|
MinidumpContextX86Writer context_x86_writer_0;
|
||||||
|
InitializeMinidumpContextX86(context_x86_writer_0.context(), kSeed0);
|
||||||
|
thread_writer_0.SetContext(&context_x86_writer_0);
|
||||||
|
|
||||||
|
thread_list_writer.AddThread(&thread_writer_0);
|
||||||
|
|
||||||
|
const uint32_t kThreadID1 = 2222222;
|
||||||
|
const uint32_t kSuspendCount1 = 222222;
|
||||||
|
const uint32_t kPriorityClass1 = 22222;
|
||||||
|
const uint32_t kPriority1 = 2222;
|
||||||
|
const uint64_t kTEB1 = 222;
|
||||||
|
const uint64_t kMemoryBase1 = 0x2220;
|
||||||
|
const size_t kMemorySize1 = 32;
|
||||||
|
const uint8_t kMemoryValue1 = 22;
|
||||||
|
const uint32_t kSeed1 = 2;
|
||||||
|
|
||||||
|
MinidumpThreadWriter thread_writer_1;
|
||||||
|
thread_writer_1.SetThreadID(kThreadID1);
|
||||||
|
thread_writer_1.SetSuspendCount(kSuspendCount1);
|
||||||
|
thread_writer_1.SetPriorityClass(kPriorityClass1);
|
||||||
|
thread_writer_1.SetPriority(kPriority1);
|
||||||
|
thread_writer_1.SetTEB(kTEB1);
|
||||||
|
|
||||||
|
TestMinidumpMemoryWriter memory_writer_1(
|
||||||
|
kMemoryBase1, kMemorySize1, kMemoryValue1);
|
||||||
|
thread_writer_1.SetStack(&memory_writer_1);
|
||||||
|
|
||||||
|
MinidumpContextX86Writer context_x86_writer_1;
|
||||||
|
InitializeMinidumpContextX86(context_x86_writer_1.context(), kSeed1);
|
||||||
|
thread_writer_1.SetContext(&context_x86_writer_1);
|
||||||
|
|
||||||
|
thread_list_writer.AddThread(&thread_writer_1);
|
||||||
|
|
||||||
|
const uint32_t kThreadID2 = 3333333;
|
||||||
|
const uint32_t kSuspendCount2 = 333333;
|
||||||
|
const uint32_t kPriorityClass2 = 33333;
|
||||||
|
const uint32_t kPriority2 = 3333;
|
||||||
|
const uint64_t kTEB2 = 333;
|
||||||
|
const uint64_t kMemoryBase2 = 0x3330;
|
||||||
|
const size_t kMemorySize2 = 48;
|
||||||
|
const uint8_t kMemoryValue2 = 33;
|
||||||
|
const uint32_t kSeed2 = 3;
|
||||||
|
|
||||||
|
MinidumpThreadWriter thread_writer_2;
|
||||||
|
thread_writer_2.SetThreadID(kThreadID2);
|
||||||
|
thread_writer_2.SetSuspendCount(kSuspendCount2);
|
||||||
|
thread_writer_2.SetPriorityClass(kPriorityClass2);
|
||||||
|
thread_writer_2.SetPriority(kPriority2);
|
||||||
|
thread_writer_2.SetTEB(kTEB2);
|
||||||
|
|
||||||
|
TestMinidumpMemoryWriter memory_writer_2(
|
||||||
|
kMemoryBase2, kMemorySize2, kMemoryValue2);
|
||||||
|
thread_writer_2.SetStack(&memory_writer_2);
|
||||||
|
|
||||||
|
MinidumpContextX86Writer context_x86_writer_2;
|
||||||
|
InitializeMinidumpContextX86(context_x86_writer_2.context(), kSeed2);
|
||||||
|
thread_writer_2.SetContext(&context_x86_writer_2);
|
||||||
|
|
||||||
|
thread_list_writer.AddThread(&thread_writer_2);
|
||||||
|
|
||||||
|
minidump_file_writer.AddStream(&thread_list_writer);
|
||||||
|
minidump_file_writer.AddStream(&memory_list_writer);
|
||||||
|
|
||||||
|
StringFileWriter file_writer;
|
||||||
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||||
|
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +
|
||||||
|
sizeof(MINIDUMP_THREAD_LIST) + 3 * sizeof(MINIDUMP_THREAD) +
|
||||||
|
sizeof(MINIDUMP_MEMORY_LIST) +
|
||||||
|
3 * sizeof(MINIDUMP_MEMORY_DESCRIPTOR) +
|
||||||
|
3 * sizeof(MinidumpContextX86) + kMemorySize0 + kMemorySize1 +
|
||||||
|
kMemorySize2 + 12, // 12 for alignment
|
||||||
|
file_writer.string().size());
|
||||||
|
|
||||||
|
const MINIDUMP_THREAD_LIST* thread_list;
|
||||||
|
const MINIDUMP_MEMORY_LIST* memory_list;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
GetThreadListStream(file_writer.string(), &thread_list, &memory_list));
|
||||||
|
|
||||||
|
EXPECT_EQ(3u, thread_list->NumberOfThreads);
|
||||||
|
EXPECT_EQ(3u, memory_list->NumberOfMemoryRanges);
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("thread 0");
|
||||||
|
|
||||||
|
MINIDUMP_THREAD expected = {};
|
||||||
|
expected.ThreadId = kThreadID0;
|
||||||
|
expected.SuspendCount = kSuspendCount0;
|
||||||
|
expected.PriorityClass = kPriorityClass0;
|
||||||
|
expected.Priority = kPriority0;
|
||||||
|
expected.Teb = kTEB0;
|
||||||
|
expected.Stack.StartOfMemoryRange = kMemoryBase0;
|
||||||
|
expected.Stack.Memory.DataSize = kMemorySize0;
|
||||||
|
expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
|
||||||
|
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack;
|
||||||
|
const MinidumpContextX86* observed_context;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectThread(&expected,
|
||||||
|
&thread_list->Threads[0],
|
||||||
|
file_writer.string(),
|
||||||
|
&observed_stack,
|
||||||
|
reinterpret_cast<const void**>(&observed_context)));
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
|
||||||
|
observed_stack,
|
||||||
|
file_writer.string(),
|
||||||
|
kMemoryValue0,
|
||||||
|
false));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed0, observed_context));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
|
||||||
|
observed_stack, &memory_list->MemoryRanges[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("thread 1");
|
||||||
|
|
||||||
|
MINIDUMP_THREAD expected = {};
|
||||||
|
expected.ThreadId = kThreadID1;
|
||||||
|
expected.SuspendCount = kSuspendCount1;
|
||||||
|
expected.PriorityClass = kPriorityClass1;
|
||||||
|
expected.Priority = kPriority1;
|
||||||
|
expected.Teb = kTEB1;
|
||||||
|
expected.Stack.StartOfMemoryRange = kMemoryBase1;
|
||||||
|
expected.Stack.Memory.DataSize = kMemorySize1;
|
||||||
|
expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
|
||||||
|
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack;
|
||||||
|
const MinidumpContextX86* observed_context;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectThread(&expected,
|
||||||
|
&thread_list->Threads[1],
|
||||||
|
file_writer.string(),
|
||||||
|
&observed_stack,
|
||||||
|
reinterpret_cast<const void**>(&observed_context)));
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
|
||||||
|
observed_stack,
|
||||||
|
file_writer.string(),
|
||||||
|
kMemoryValue1,
|
||||||
|
false));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed1, observed_context));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
|
||||||
|
observed_stack, &memory_list->MemoryRanges[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("thread 2");
|
||||||
|
|
||||||
|
MINIDUMP_THREAD expected = {};
|
||||||
|
expected.ThreadId = kThreadID2;
|
||||||
|
expected.SuspendCount = kSuspendCount2;
|
||||||
|
expected.PriorityClass = kPriorityClass2;
|
||||||
|
expected.Priority = kPriority2;
|
||||||
|
expected.Teb = kTEB2;
|
||||||
|
expected.Stack.StartOfMemoryRange = kMemoryBase2;
|
||||||
|
expected.Stack.Memory.DataSize = kMemorySize2;
|
||||||
|
expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
|
||||||
|
|
||||||
|
const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack;
|
||||||
|
const MinidumpContextX86* observed_context;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectThread(&expected,
|
||||||
|
&thread_list->Threads[2],
|
||||||
|
file_writer.string(),
|
||||||
|
&observed_stack,
|
||||||
|
reinterpret_cast<const void**>(&observed_context)));
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
|
||||||
|
observed_stack,
|
||||||
|
file_writer.string(),
|
||||||
|
kMemoryValue2,
|
||||||
|
true));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpContextX86(kSeed2, observed_context));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
|
||||||
|
observed_stack, &memory_list->MemoryRanges[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpThreadWriterDeathTest, NoContext) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
MinidumpThreadListWriter thread_list_writer;
|
||||||
|
|
||||||
|
MinidumpThreadWriter thread_writer;
|
||||||
|
|
||||||
|
thread_list_writer.AddThread(&thread_writer);
|
||||||
|
minidump_file_writer.AddStream(&thread_list_writer);
|
||||||
|
|
||||||
|
StringFileWriter file_writer;
|
||||||
|
ASSERT_DEATH(minidump_file_writer.WriteEverything(&file_writer), "context_");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
Loading…
x
Reference in New Issue
Block a user