Write MINIDUMP_HANDLE_DATA_STREAM to minidump

R=mark@chromium.org
BUG=crashpad:21, crashpad:52

Review URL: https://codereview.chromium.org/1419623003 .
This commit is contained in:
Scott Graham 2015-10-21 10:43:42 -07:00
parent 3ac40a54d0
commit 3261edd997
14 changed files with 391 additions and 3 deletions

View File

@ -157,6 +157,9 @@ enum MINIDUMP_STREAM_TYPE {
//! \brief The stream type for MINIDUMP_SYSTEM_INFO.
SystemInfoStream = 7,
//! \brief The stream contains information about active `HANDLE`s.
HandleDataStream = 12,
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
//!

View File

@ -44,6 +44,8 @@
'minidump_extensions.h',
'minidump_file_writer.cc',
'minidump_file_writer.h',
'minidump_handle_writer.cc',
'minidump_handle_writer.h',
'minidump_memory_info_writer.cc',
'minidump_memory_info_writer.h',
'minidump_memory_writer.cc',

View File

@ -71,6 +71,11 @@ enum MinidumpStreamType : uint32_t {
//! \sa SystemInfoStream
kMinidumpStreamTypeSystemInfo = SystemInfoStream,
//! \brief The stream type for MINIDUMP_HANDLE_DATA_STREAM.
//!
//! \sa HandleDataStream
kMinidumpStreamTypeHandleData = HandleDataStream,
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
//!

View File

@ -17,6 +17,7 @@
#include "base/logging.h"
#include "minidump/minidump_crashpad_info_writer.h"
#include "minidump/minidump_exception_writer.h"
#include "minidump/minidump_handle_writer.h"
#include "minidump/minidump_memory_info_writer.h"
#include "minidump/minidump_memory_writer.h"
#include "minidump/minidump_misc_info_writer.h"
@ -108,6 +109,13 @@ void MinidumpFileWriter::InitializeFromSnapshot(
AddStream(memory_info_list.Pass());
}
std::vector<HandleSnapshot> handles_snapshot = process_snapshot->Handles();
if (!handles_snapshot.empty()) {
auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter());
handle_data_writer->InitializeFromSnapshot(handles_snapshot);
AddStream(handle_data_writer.Pass());
}
memory_list->AddFromSnapshot(process_snapshot->ExtraMemory());
AddStream(memory_list.Pass());

View File

@ -0,0 +1,115 @@
// Copyright 2015 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_handle_writer.h"
#include <string>
#include "base/logging.h"
#include "minidump/minidump_extensions.h"
#include "util/file/file_writer.h"
namespace crashpad {
MinidumpHandleDataWriter::MinidumpHandleDataWriter()
: handle_data_stream_base_(), handle_descriptors_(), strings_() {
}
MinidumpHandleDataWriter::~MinidumpHandleDataWriter() {
}
void MinidumpHandleDataWriter::InitializeFromSnapshot(
const std::vector<HandleSnapshot>& handle_snapshots) {
DCHECK_EQ(state(), kStateMutable);
DCHECK(handle_descriptors_.empty());
// Because we RegisterRVA() on the string writer below, we preallocate and
// never resize the handle_descriptors_ vector.
handle_descriptors_.resize(handle_snapshots.size());
strings_.reserve(handle_snapshots.size());
for (size_t i = 0; i < handle_snapshots.size(); ++i) {
const HandleSnapshot& handle_snapshot = handle_snapshots[i];
MINIDUMP_HANDLE_DESCRIPTOR& descriptor = handle_descriptors_[i];
descriptor.Handle = handle_snapshot.handle;
if (handle_snapshot.type_name.empty()) {
descriptor.TypeNameRva = 0;
} else {
// TODO(scottmg): There is often a number of repeated type names here, the
// strings ought to be pooled.
strings_.push_back(new internal::MinidumpUTF16StringWriter());
strings_.back()->SetUTF16(handle_snapshot.type_name);
strings_.back()->RegisterRVA(&descriptor.TypeNameRva);
}
descriptor.ObjectNameRva = 0;
descriptor.Attributes = handle_snapshot.attributes;
descriptor.GrantedAccess = handle_snapshot.granted_access;
descriptor.HandleCount = handle_snapshot.handle_count;
descriptor.PointerCount = handle_snapshot.pointer_count;
}
}
bool MinidumpHandleDataWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
if (!MinidumpStreamWriter::Freeze())
return false;
handle_data_stream_base_.SizeOfHeader = sizeof(handle_data_stream_base_);
handle_data_stream_base_.SizeOfDescriptor = sizeof(handle_descriptors_[0]);
handle_data_stream_base_.NumberOfDescriptors = handle_descriptors_.size();
handle_data_stream_base_.Reserved = 0;
return true;
}
size_t MinidumpHandleDataWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
return sizeof(handle_data_stream_base_) +
sizeof(handle_descriptors_[0]) * handle_descriptors_.size();
}
std::vector<internal::MinidumpWritable*> MinidumpHandleDataWriter::Children() {
DCHECK_GE(state(), kStateFrozen);
std::vector<MinidumpWritable*> children;
for (auto* string : strings_)
children.push_back(string);
return children;
}
bool MinidumpHandleDataWriter::WriteObject(FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
WritableIoVec iov;
iov.iov_base = &handle_data_stream_base_;
iov.iov_len = sizeof(handle_data_stream_base_);
std::vector<WritableIoVec> iovecs(1, iov);
for (const auto& descriptor : handle_descriptors_) {
iov.iov_base = &descriptor;
iov.iov_len = sizeof(descriptor);
iovecs.push_back(iov);
}
return file_writer->WriteIoVec(&iovecs);
}
MinidumpStreamType MinidumpHandleDataWriter::StreamType() const {
return kMinidumpStreamTypeHandleData;
}
} // namespace crashpad

View File

@ -0,0 +1,75 @@
// Copyright 2015 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_HANDLE_WRITER_H_
#define CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_
#include <windows.h>
#include <dbghelp.h>
#include <vector>
#include "minidump/minidump_stream_writer.h"
#include "minidump/minidump_string_writer.h"
#include "minidump/minidump_writable.h"
#include "snapshot/handle_snapshot.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad {
//! \brief The writer for a MINIDUMP_HANDLE_DATA_STREAM stream in a minidump
//! and its contained MINIDUMP_HANDLE_DESCRIPTOR s.
//!
//! As we currently do not track any data beyond what MINIDUMP_HANDLE_DESCRIPTOR
//! supports, we only write that type of record rather than the newer
//! MINIDUMP_HANDLE_DESCRIPTOR_2.
//!
//! Note that this writer writes both the header (MINIDUMP_HANDLE_DATA_STREAM)
//! and the list of objects (MINIDUMP_HANDLE_DESCRIPTOR), which is different
//! from some of the other list writers.
class MinidumpHandleDataWriter final : public internal::MinidumpStreamWriter {
public:
MinidumpHandleDataWriter();
~MinidumpHandleDataWriter() override;
//! \brief Adds a MINIDUMP_HANDLE_DESCRIPTOR for each handle in \a
//! handle_snapshot to the MINIDUMP_HANDLE_DATA_STREAM.
//!
//! \param[in] handle_snapshots The handle snapshots to use as source data.
//!
//! \note Valid in #kStateMutable.
void InitializeFromSnapshot(
const std::vector<HandleSnapshot>& handle_snapshots);
protected:
// MinidumpWritable:
bool Freeze() override;
size_t SizeOfObject() override;
std::vector<MinidumpWritable*> Children() override;
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpStreamWriter:
MinidumpStreamType StreamType() const override;
private:
MINIDUMP_HANDLE_DATA_STREAM handle_data_stream_base_;
std::vector<MINIDUMP_HANDLE_DESCRIPTOR> handle_descriptors_;
PointerVector<internal::MinidumpUTF16StringWriter> strings_;
DISALLOW_COPY_AND_ASSIGN(MinidumpHandleDataWriter);
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_

View File

@ -0,0 +1,127 @@
// Copyright 2015 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_handle_writer.h"
#include <string>
#include "gtest/gtest.h"
#include "minidump/minidump_file_writer.h"
#include "minidump/test/minidump_file_writer_test_util.h"
#include "minidump/test/minidump_string_writer_test_util.h"
#include "minidump/test/minidump_writable_test_util.h"
#include "util/file/string_file.h"
namespace crashpad {
namespace test {
namespace {
// The handle data stream is expected to be the only stream.
void GetHandleDataStream(
const std::string& file_contents,
const MINIDUMP_HANDLE_DATA_STREAM** handle_data_stream) {
const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
const size_t kHandleDataStreamOffset =
kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
const MINIDUMP_DIRECTORY* directory;
const MINIDUMP_HEADER* header =
MinidumpHeaderAtStart(file_contents, &directory);
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
ASSERT_TRUE(directory);
const size_t kDirectoryIndex = 0;
ASSERT_EQ(kMinidumpStreamTypeHandleData,
directory[kDirectoryIndex].StreamType);
EXPECT_EQ(kHandleDataStreamOffset, directory[kDirectoryIndex].Location.Rva);
*handle_data_stream =
MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(
file_contents, directory[kDirectoryIndex].Location);
ASSERT_TRUE(*handle_data_stream);
}
TEST(MinidumpHandleDataWriter, Empty) {
MinidumpFileWriter minidump_file_writer;
auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter());
minidump_file_writer.AddStream(handle_data_writer.Pass());
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
sizeof(MINIDUMP_HANDLE_DATA_STREAM),
string_file.string().size());
const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;
ASSERT_NO_FATAL_FAILURE(
GetHandleDataStream(string_file.string(), &handle_data_stream));
EXPECT_EQ(0u, handle_data_stream->NumberOfDescriptors);
}
TEST(MinidumpHandleDataWriter, OneHandle) {
MinidumpFileWriter minidump_file_writer;
auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter());
HandleSnapshot handle_snapshot;
handle_snapshot.handle = 0x1234;
handle_snapshot.type_name = L"Something";
handle_snapshot.attributes = 0x12345678;
handle_snapshot.granted_access = 0x9abcdef0;
handle_snapshot.pointer_count = 4567;
handle_snapshot.handle_count = 9876;
std::vector<HandleSnapshot> snapshot;
snapshot.push_back(handle_snapshot);
handle_data_writer->InitializeFromSnapshot(snapshot);
minidump_file_writer.AddStream(handle_data_writer.Pass());
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
const size_t kTypeNameStringDataLength =
(handle_snapshot.type_name.size() + 1) *
sizeof(handle_snapshot.type_name[0]);
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
sizeof(MINIDUMP_HANDLE_DATA_STREAM) +
sizeof(MINIDUMP_HANDLE_DESCRIPTOR) + sizeof(MINIDUMP_STRING) +
kTypeNameStringDataLength,
string_file.string().size());
const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;
ASSERT_NO_FATAL_FAILURE(
GetHandleDataStream(string_file.string(), &handle_data_stream));
EXPECT_EQ(1u, handle_data_stream->NumberOfDescriptors);
const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor =
reinterpret_cast<const MINIDUMP_HANDLE_DESCRIPTOR*>(
&handle_data_stream[1]);
EXPECT_EQ(handle_snapshot.handle, handle_descriptor->Handle);
EXPECT_EQ(handle_snapshot.type_name,
MinidumpStringAtRVAAsString(string_file.string(),
handle_descriptor->TypeNameRva));
EXPECT_EQ(0u, handle_descriptor->ObjectNameRva);
EXPECT_EQ(handle_snapshot.attributes, handle_descriptor->Attributes);
EXPECT_EQ(handle_snapshot.granted_access, handle_descriptor->GrantedAccess);
EXPECT_EQ(handle_snapshot.handle_count, handle_descriptor->HandleCount);
EXPECT_EQ(handle_snapshot.pointer_count, handle_descriptor->PointerCount);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -51,7 +51,7 @@ void GetMemoryInfoListStream(
*memory_info_list =
MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
file_contents, directory[kDirectoryIndex].Location);
ASSERT_TRUE(memory_info_list);
ASSERT_TRUE(*memory_info_list);
}
TEST(MinidumpMemoryInfoWriter, Empty) {

View File

@ -99,6 +99,13 @@ class MinidumpUTF16StringWriter final
//! \note Valid in #kStateMutable.
void SetUTF8(const std::string& string_utf8);
//! \brief Sets the given UTF-16 string as the string to be written.
//!
//! \note Valid in #kStateMutable.
void SetUTF16(const base::string16& string_utf16) {
set_string(string_utf16);
}
private:
DISALLOW_COPY_AND_ASSIGN(MinidumpUTF16StringWriter);
};

View File

@ -53,6 +53,23 @@ TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) {
MinidumpStringAtRVAAsString(string_file.string(), 0));
}
{
SCOPED_TRACE("no conversion");
string_file.Reset();
crashpad::internal::MinidumpUTF16StringWriter string_writer;
const base::string16 kString(L"oóöőo");
string_writer.SetUTF16(kString);
EXPECT_TRUE(string_writer.WriteEverything(&string_file));
ASSERT_EQ(
sizeof(MINIDUMP_STRING) + (kString.size() + 1) * sizeof(kString[0]),
string_file.string().size());
const MINIDUMP_STRING* minidump_string =
MinidumpStringAtRVA(string_file.string(), 0);
EXPECT_TRUE(minidump_string);
EXPECT_EQ(kString, MinidumpStringAtRVAAsString(string_file.string(), 0));
}
const struct {
size_t input_length;
const char* input_string;

View File

@ -36,6 +36,7 @@
'minidump_context_writer_test.cc',
'minidump_crashpad_info_writer_test.cc',
'minidump_exception_writer_test.cc',
'minidump_handle_writer_test.cc',
'minidump_file_writer_test.cc',
'minidump_memory_info_writer_test.cc',
'minidump_memory_writer_test.cc',

View File

@ -181,6 +181,14 @@ struct MinidumpThreadListTraits {
}
};
struct MinidumpHandleDataStreamTraits {
using ListType = MINIDUMP_HANDLE_DATA_STREAM;
enum : size_t { kElementSize = sizeof(MINIDUMP_HANDLE_DESCRIPTOR) };
static size_t ElementCount(const ListType* list) {
return static_cast<size_t>(list->NumberOfDescriptors);
}
};
struct MinidumpMemoryInfoListTraits {
using ListType = MINIDUMP_MEMORY_INFO_LIST;
enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) };
@ -252,6 +260,14 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
file_contents, location);
}
template <>
const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpHandleDataStreamTraits>(
file_contents, location);
}
template <>
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,

View File

@ -90,6 +90,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList);
@ -190,6 +191,11 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
MINIDUMP_THREAD_LIST>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,

View File

@ -183,10 +183,14 @@ def RunTests(cdb_path, dump_path, pipe_name):
out = CdbRun(cdb_path, dump_path, '!locks')
out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::'
r'g_test_critical_section', 'lock was captured')
if float(platform.win32_ver()[0]) != 7:
if platform.win32_ver()[0] != '7':
# We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7.
out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked')
out = CdbRun(cdb_path, dump_path, '!handle')
out.Check(r'\d+ Handles', 'captured handles')
out.Check(r'Event\s+\d+', 'capture some event handles')
out.Check(r'File\s+\d+', 'capture some file handles')
def main(args):
try:
@ -202,8 +206,10 @@ def main(args):
# Make sure we can download Windows symbols.
if not os.environ.get('_NT_SYMBOL_PATH'):
symbol_dir = MakeTempDir()
protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http'
os.environ['_NT_SYMBOL_PATH'] = (
'SRV*' + symbol_dir + '*https://msdl.microsoft.com/download/symbols')
'SRV*' + symbol_dir + '*' +
protocol + '://msdl.microsoft.com/download/symbols')
pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (
os.getpid(), str(random.getrandbits(64)))