snapshot: Add a minimal ModuleSnapshotMinidump and accessor from

ProcessSnapshotMinidump.

ModuleSnapshotMinidump is currently only capable of reading module
annotations, in both vector and simple-dictionary forms. It does not
read any other module information from minidump files. These annotations
are all that are necessary to be able to upload Crashpad-produced
minidumps to Breakpad crash processor servers, because Breakpad accepts
them as HTTP POST parameters, while Crashpad places them in the minidump
file itself.

TEST=snapshot_test ProcessSnapshotMinidump.Modules
BUG=crashpad:10
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/972383002
This commit is contained in:
Mark Mentovai 2015-03-04 12:29:01 -05:00
parent 4539fd1553
commit 445c0eae7c
12 changed files with 758 additions and 84 deletions

View File

@ -136,6 +136,7 @@ void ModuleSnapshotMac::UUID(crashpad::UUID* uuid) const {
}
std::vector<std::string> ModuleSnapshotMac::AnnotationsVector() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
MachOImageAnnotationsReader annotations_reader(
process_reader_, mach_o_image_reader_, name_);
return annotations_reader.Vector();
@ -143,6 +144,7 @@ std::vector<std::string> ModuleSnapshotMac::AnnotationsVector() const {
std::map<std::string, std::string> ModuleSnapshotMac::AnnotationsSimpleMap()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
MachOImageAnnotationsReader annotations_reader(
process_reader_, mach_o_image_reader_, name_);
return annotations_reader.SimpleMap();

View File

@ -144,10 +144,9 @@ void ProcessSnapshotMac::InitializeThreads() {
process_reader_.Threads();
for (const ProcessReader::Thread& process_reader_thread :
process_reader_threads) {
internal::ThreadSnapshotMac* thread = new internal::ThreadSnapshotMac();
threads_.push_back(thread);
if (!thread->Initialize(&process_reader_, process_reader_thread)) {
threads_.pop_back();
auto thread = make_scoped_ptr(new internal::ThreadSnapshotMac());
if (thread->Initialize(&process_reader_, process_reader_thread)) {
threads_.push_back(thread.release());
}
}
}
@ -157,10 +156,9 @@ void ProcessSnapshotMac::InitializeModules() {
process_reader_.Modules();
for (const ProcessReader::Module& process_reader_module :
process_reader_modules) {
internal::ModuleSnapshotMac* module = new internal::ModuleSnapshotMac();
modules_.push_back(module);
if (!module->Initialize(&process_reader_, process_reader_module)) {
modules_.pop_back();
auto module = make_scoped_ptr(new internal::ModuleSnapshotMac());
if (module->Initialize(&process_reader_, process_reader_module)) {
modules_.push_back(module.release());
}
}
}

View File

@ -48,7 +48,8 @@ bool ReadMinidumpSimpleStringDictionary(
}
if (location.DataSize !=
entry_count * sizeof(MinidumpSimpleStringDictionaryEntry)) {
sizeof(MinidumpSimpleStringDictionary) +
entry_count * sizeof(MinidumpSimpleStringDictionaryEntry)) {
LOG(ERROR) << "simple_string_dictionary size mismatch";
return false;
}
@ -63,19 +64,17 @@ bool ReadMinidumpSimpleStringDictionary(
for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {
std::string key;
if (!ReadMinidumpUTF8String(file_reader, entry.key, &key)) {
// Not a hard error, keep trying.
continue;
return false;
}
std::string value;
if (!ReadMinidumpUTF8String(file_reader, entry.value, &value)) {
// Not a hard error, keep trying.
continue;
return false;
}
if (local_dictionary.find(key) != local_dictionary.end()) {
LOG(WARNING) << "duplicate key " << key << ", discarding value " << value;
continue;
return false;
}
local_dictionary.insert(std::make_pair(key, value));

View File

@ -30,9 +30,7 @@ namespace internal {
//! location in \a file_reader, and returns it in \a dictionary.
//!
//! \return `true` on success, with \a dictionary set by replacing its contents.
//! `false` on failure, with a message logged. If any individual strings
//! cannot be read, or if any duplicate keys are found, it is not considered
//! an error, although a message will be logged.
//! `false` on failure, with a message logged.
bool ReadMinidumpSimpleStringDictionary(
FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR& location,

View File

@ -0,0 +1,72 @@
// 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 "snapshot/minidump/minidump_string_list_reader.h"
#include "base/logging.h"
#include "minidump/minidump_extensions.h"
#include "snapshot/minidump/minidump_string_reader.h"
namespace crashpad {
namespace internal {
bool ReadMinidumpStringList(FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR& location,
std::vector<std::string>* list) {
if (location.Rva == 0) {
list->clear();
return true;
}
if (location.DataSize < sizeof(MinidumpRVAList)) {
LOG(ERROR) << "string_list size mismatch";
return false;
}
if (!file_reader->SeekSet(location.Rva)) {
return false;
}
uint32_t entry_count;
if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) {
return false;
}
if (location.DataSize !=
sizeof(MinidumpRVAList) + entry_count * sizeof(RVA)) {
LOG(ERROR) << "string_list size mismatch";
return false;
}
std::vector<RVA> rvas(entry_count);
if (!file_reader->ReadExactly(&rvas[0], entry_count * sizeof(rvas[0]))) {
return false;
}
std::vector<std::string> local_list;
for (RVA rva : rvas) {
std::string element;
if (!ReadMinidumpUTF8String(file_reader, rva, &element)) {
return false;
}
local_list.push_back(element);
}
list->swap(local_list);
return true;
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,42 @@
// 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_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_
#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_
#include <windows.h>
#include <dbghelp.h>
#include <string>
#include <vector>
#include "util/file/file_reader.h"
namespace crashpad {
namespace internal {
//! \brief Reads a list of MinidumpUTF8String objects in a MinidumpRVAList from
//! a minidump file \a location in \a file_reader, and returns it in \a
//! list.
//!
//! \return `true` on success, with \a list set by replacing its contents.
//! `false` on failure, with a message logged.
bool ReadMinidumpStringList(FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR& location,
std::vector<std::string>* list);
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_

View File

@ -0,0 +1,174 @@
// 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 "snapshot/minidump/module_snapshot_minidump.h"
#include "minidump/minidump_extensions.h"
#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
#include "snapshot/minidump/minidump_string_list_reader.h"
namespace crashpad {
namespace internal {
ModuleSnapshotMinidump::ModuleSnapshotMinidump()
: ModuleSnapshot(),
minidump_module_(),
annotations_vector_(),
annotations_simple_map_(),
initialized_() {
}
ModuleSnapshotMinidump::~ModuleSnapshotMinidump() {
}
bool ModuleSnapshotMinidump::Initialize(
FileReaderInterface* file_reader,
RVA minidump_module_rva,
const MINIDUMP_LOCATION_DESCRIPTOR*
minidump_module_crashpad_info_location) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
if (!file_reader->SeekSet(minidump_module_rva)) {
return false;
}
if (!file_reader->ReadExactly(&minidump_module_, sizeof(minidump_module_))) {
return false;
}
if (!InitializeModuleCrashpadInfo(file_reader,
minidump_module_crashpad_info_location)) {
return false;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
std::string ModuleSnapshotMinidump::Name() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return std::string();
}
uint64_t ModuleSnapshotMinidump::Address() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return 0;
}
uint64_t ModuleSnapshotMinidump::Size() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return 0;
}
time_t ModuleSnapshotMinidump::Timestamp() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return 0;
}
void ModuleSnapshotMinidump::FileVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
*version_0 = 0;
*version_1 = 0;
*version_2 = 0;
*version_3 = 0;
}
void ModuleSnapshotMinidump::SourceVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
*version_0 = 0;
*version_1 = 0;
*version_2 = 0;
*version_3 = 0;
}
ModuleSnapshot::ModuleType ModuleSnapshotMinidump::GetModuleType() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return kModuleTypeUnknown;
}
void ModuleSnapshotMinidump::UUID(crashpad::UUID* uuid) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
*uuid = crashpad::UUID();
}
std::vector<std::string> ModuleSnapshotMinidump::AnnotationsVector() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return annotations_vector_;
}
std::map<std::string, std::string>
ModuleSnapshotMinidump::AnnotationsSimpleMap() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return annotations_simple_map_;
}
bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo(
FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR*
minidump_module_crashpad_info_location) {
if (!minidump_module_crashpad_info_location ||
minidump_module_crashpad_info_location->Rva == 0) {
return true;
}
MinidumpModuleCrashpadInfo minidump_module_crashpad_info;
if (minidump_module_crashpad_info_location->DataSize <
sizeof(minidump_module_crashpad_info)) {
LOG(ERROR) << "minidump_module_crashpad_info size mismatch";
return false;
}
if (!file_reader->SeekSet(minidump_module_crashpad_info_location->Rva)) {
return false;
}
if (!file_reader->ReadExactly(&minidump_module_crashpad_info,
sizeof(minidump_module_crashpad_info))) {
return false;
}
if (minidump_module_crashpad_info.version !=
MinidumpModuleCrashpadInfo::kVersion) {
LOG(ERROR) << "minidump_module_crashpad_info version mismatch";
return false;
}
if (!ReadMinidumpStringList(file_reader,
minidump_module_crashpad_info.list_annotations,
&annotations_vector_)) {
return false;
}
return ReadMinidumpSimpleStringDictionary(
file_reader,
minidump_module_crashpad_info.simple_annotations,
&annotations_simple_map_);
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,97 @@
// 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_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_
#define CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_
#include <windows.h>
#include <dbghelp.h>
#include <stdint.h>
#include <sys/types.h>
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "snapshot/module_snapshot.h"
#include "util/file/file_reader.h"
#include "util/misc/initialization_state_dcheck.h"
namespace crashpad {
namespace internal {
//! \brief A ModuleSnapshot based on a module in a minidump file.
class ModuleSnapshotMinidump final : public ModuleSnapshot {
public:
ModuleSnapshotMinidump();
~ModuleSnapshotMinidump() override;
//! \brief Initializes the object.
//!
//! \param[in] file_reader A file reader corresponding to a minidump file.
//! The file reader must support seeking.
//! \param[in] minidump_module_rva The file offset in \a file_reader at which
//! the modules MINIDUMP_MODULE structure is located.
//! \param[in] minidump_crashpad_module_info_location The location in \a
//! file_reader at which the modules corresponding
//! MinidumpModuleCrashpadInfo structure is located. If no such
//! corresponding structure is available for a module, this may be
//! `nullptr`.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool Initialize(FileReaderInterface* file_reader,
RVA minidump_module_rva,
const MINIDUMP_LOCATION_DESCRIPTOR*
minidump_crashpad_module_info_location);
// ModuleSnapshot:
std::string Name() const override;
uint64_t Address() const override;
uint64_t Size() const override;
time_t Timestamp() const override;
void FileVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const override;
void SourceVersion(uint16_t* version_0,
uint16_t* version_1,
uint16_t* version_2,
uint16_t* version_3) const override;
ModuleType GetModuleType() const override;
void UUID(crashpad::UUID* uuid) const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
private:
// Initializes data carried in a MinidumpModuleCrashpadInfo structure on
// behalf of Initialize().
bool InitializeModuleCrashpadInfo(FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR*
minidump_module_crashpad_info_location);
MINIDUMP_MODULE minidump_module_;
std::vector<std::string> annotations_vector_;
std::map<std::string, std::string> annotations_simple_map_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMinidump);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_

View File

@ -14,6 +14,9 @@
#include "snapshot/minidump/process_snapshot_minidump.h"
#include <utility>
#include "base/memory/scoped_ptr.h"
#include "util/file/file_io.h"
#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
@ -24,6 +27,7 @@ ProcessSnapshotMinidump::ProcessSnapshotMinidump()
header_(),
stream_directory_(),
stream_map_(),
modules_(),
crashpad_info_(),
annotations_simple_map_(),
file_reader_(nullptr),
@ -80,33 +84,35 @@ bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) {
INITIALIZATION_STATE_SET_VALID(initialized_);
InitializeCrashpadInfo();
if (!InitializeCrashpadInfo()) {
return false;
}
return true;
return InitializeModules();
}
pid_t ProcessSnapshotMinidump::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return 0;
}
pid_t ProcessSnapshotMinidump::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return 0;
}
void ProcessSnapshotMinidump::SnapshotTime(timeval* snapshot_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
snapshot_time->tv_sec = 0;
snapshot_time->tv_usec = 0;
}
void ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
start_time->tv_sec = 0;
start_time->tv_usec = 0;
}
@ -114,7 +120,7 @@ void ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const {
void ProcessSnapshotMinidump::ProcessCPUTimes(timeval* user_time,
timeval* system_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
user_time->tv_sec = 0;
user_time->tv_usec = 0;
system_time->tv_sec = 0;
@ -128,62 +134,185 @@ ProcessSnapshotMinidump::AnnotationsSimpleMap() const {
// annotations_simple_map_ to be lazily constructed: InitializeCrashpadInfo()
// could be called here, and from other locations that require it, rather than
// calling it from Initialize().
// https://code.google.com/p/crashpad/issues/detail?id=9
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return annotations_simple_map_;
}
const SystemSnapshot* ProcessSnapshotMinidump::System() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return nullptr;
}
std::vector<const ThreadSnapshot*> ProcessSnapshotMinidump::Threads() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return std::vector<const ThreadSnapshot*>();
}
std::vector<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
return std::vector<const ModuleSnapshot*>();
std::vector<const ModuleSnapshot*> modules;
for (internal::ModuleSnapshotMinidump* module : modules_) {
modules.push_back(module);
}
return modules;
}
const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return nullptr;
}
void ProcessSnapshotMinidump::InitializeCrashpadInfo() {
const auto& it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo);
if (it == stream_map_.end()) {
return;
bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
const auto& stream_it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo);
if (stream_it == stream_map_.end()) {
return true;
}
if (it->second->DataSize < sizeof(crashpad_info_)) {
if (stream_it->second->DataSize < sizeof(crashpad_info_)) {
LOG(ERROR) << "crashpad_info size mismatch";
return;
return false;
}
if (!file_reader_->SeekSet(it->second->Rva)) {
return;
if (!file_reader_->SeekSet(stream_it->second->Rva)) {
return false;
}
if (!file_reader_->ReadExactly(&crashpad_info_, sizeof(crashpad_info_))) {
return;
return false;
}
if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {
LOG(ERROR) << "crashpad_info version mismatch";
return;
return false;
}
internal::ReadMinidumpSimpleStringDictionary(
return internal::ReadMinidumpSimpleStringDictionary(
file_reader_,
crashpad_info_.simple_annotations,
&annotations_simple_map_);
}
bool ProcessSnapshotMinidump::InitializeModules() {
const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList);
if (stream_it == stream_map_.end()) {
return true;
}
std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR> module_crashpad_info_links;
if (!InitializeModulesCrashpadInfo(&module_crashpad_info_links)) {
return false;
}
if (stream_it->second->DataSize < sizeof(MINIDUMP_MODULE_LIST)) {
LOG(ERROR) << "module_list size mismatch";
return false;
}
if (!file_reader_->SeekSet(stream_it->second->Rva)) {
return false;
}
uint32_t module_count;
if (!file_reader_->ReadExactly(&module_count, sizeof(module_count))) {
return false;
}
if (sizeof(MINIDUMP_MODULE_LIST) + module_count * sizeof(MINIDUMP_MODULE) !=
stream_it->second->DataSize) {
LOG(ERROR) << "module_list size mismatch";
return false;
}
for (uint32_t module_index = 0; module_index < module_count; ++module_index) {
const RVA module_rva = stream_it->second->Rva + sizeof(module_count) +
module_index * sizeof(MINIDUMP_MODULE);
const auto& module_crashpad_info_it =
module_crashpad_info_links.find(module_index);
const MINIDUMP_LOCATION_DESCRIPTOR* module_crashpad_info_location =
module_crashpad_info_it != module_crashpad_info_links.end()
? &module_crashpad_info_it->second
: nullptr;
auto module = make_scoped_ptr(new internal::ModuleSnapshotMinidump());
if (!module->Initialize(
file_reader_, module_rva, module_crashpad_info_location)) {
return false;
}
modules_.push_back(module.release());
}
return true;
}
bool ProcessSnapshotMinidump::InitializeModulesCrashpadInfo(
std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>*
module_crashpad_info_links) {
module_crashpad_info_links->clear();
if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {
return false;
}
if (crashpad_info_.module_list.Rva == 0) {
return true;
}
if (crashpad_info_.module_list.DataSize <
sizeof(MinidumpModuleCrashpadInfoList)) {
LOG(ERROR) << "module_crashpad_info_list size mismatch";
return false;
}
if (!file_reader_->SeekSet(crashpad_info_.module_list.Rva)) {
return false;
}
uint32_t crashpad_module_count;
if (!file_reader_->ReadExactly(&crashpad_module_count,
sizeof(crashpad_module_count))) {
return false;
}
if (crashpad_info_.module_list.DataSize !=
sizeof(MinidumpModuleCrashpadInfoList) +
crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink)) {
LOG(ERROR) << "module_crashpad_info_list size mismatch";
return false;
}
scoped_ptr<MinidumpModuleCrashpadInfoLink[]> minidump_links(
new MinidumpModuleCrashpadInfoLink[crashpad_module_count]);
if (!file_reader_->ReadExactly(
&minidump_links[0],
crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink))) {
return false;
}
for (uint32_t crashpad_module_index = 0;
crashpad_module_index < crashpad_module_count;
++crashpad_module_index) {
const MinidumpModuleCrashpadInfoLink& minidump_link =
minidump_links[crashpad_module_index];
if (module_crashpad_info_links->find(
minidump_link.minidump_module_list_index) !=
module_crashpad_info_links->end()) {
LOG(WARNING)
<< "duplicate module_crashpad_info_list minidump_module_list_index "
<< minidump_link.minidump_module_list_index;
return false;
} else {
module_crashpad_info_links->insert(std::make_pair(
minidump_link.minidump_module_list_index, minidump_link.location));
}
}
return true;
}
} // namespace crashpad

View File

@ -26,12 +26,14 @@
#include "base/basictypes.h"
#include "minidump/minidump_extensions.h"
#include "snapshot/exception_snapshot.h"
#include "snapshot/minidump/module_snapshot_minidump.h"
#include "snapshot/module_snapshot.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h"
#include "snapshot/thread_snapshot.h"
#include "util/file/file_reader.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad {
@ -67,11 +69,23 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot {
private:
// Initializes data carried in a MinidumpCrashpadInfo stream on behalf of
// Initialize().
void InitializeCrashpadInfo();
bool InitializeCrashpadInfo();
// Initializes data carried in a MINIDUMP_MODULE_LIST stream on behalf of
// Initialize().
bool InitializeModules();
// Initializes data carried in a MinidumpModuleCrashpadInfoList structure on
// behalf of InitializeModules(). This makes use of MinidumpCrashpadInfo as
// well, so it must be called after InitializeCrashpadInfo().
bool InitializeModulesCrashpadInfo(
std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>*
module_crashpad_info_links);
MINIDUMP_HEADER header_;
std::vector<MINIDUMP_DIRECTORY> stream_directory_;
std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
PointerVector<internal::ModuleSnapshotMinidump> modules_;
MinidumpCrashpadInfo crashpad_info_;
std::map<std::string, std::string> annotations_simple_map_;
FileReaderInterface* file_reader_; // weak

View File

@ -20,6 +20,7 @@
#include "base/memory/scoped_ptr.h"
#include "gtest/gtest.h"
#include "snapshot/module_snapshot.h"
#include "util/file/string_file.h"
namespace crashpad {
@ -57,51 +58,84 @@ TEST(ProcessSnapshotMinidump, Empty) {
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
}
// Writes |string| to |writer| as a MinidumpUTF8String, and returns the file
// offst of the beginning of the string.
RVA WriteString(FileWriterInterface* writer, const std::string& string) {
RVA rva = static_cast<RVA>(writer->SeekGet());
uint32_t string_size = string.size();
EXPECT_TRUE(writer->Write(&string_size, sizeof(string_size)));
// Include the trailing NUL character.
EXPECT_TRUE(writer->Write(string.c_str(), string.size() + 1));
return rva;
}
// Writes |dictionary| to |writer| as a MinidumpSimpleStringDictionary, and
// populates |location| with a location descriptor identifying what was written.
void WriteMinidumpSimpleStringDictionary(
MINIDUMP_LOCATION_DESCRIPTOR* location,
FileWriterInterface* writer,
const std::map<std::string, std::string>& dictionary) {
std::vector<MinidumpSimpleStringDictionaryEntry> entries;
for (const auto& it : dictionary) {
MinidumpSimpleStringDictionaryEntry entry;
entry.key = WriteString(writer, it.first);
entry.value = WriteString(writer, it.second);
entries.push_back(entry);
}
location->Rva = static_cast<RVA>(writer->SeekGet());
const uint32_t simple_string_dictionary_entries = entries.size();
EXPECT_TRUE(writer->Write(&simple_string_dictionary_entries,
sizeof(simple_string_dictionary_entries)));
for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {
EXPECT_TRUE(writer->Write(&entry, sizeof(entry)));
}
location->DataSize =
sizeof(simple_string_dictionary_entries) +
entries.size() * sizeof(MinidumpSimpleStringDictionaryEntry);
}
// Writes |strings| to |writer| as a MinidumpRVAList referencing
// MinidumpUTF8String objects, and populates |location| with a location
// descriptor identifying what was written.
void WriteMinidumpStringList(MINIDUMP_LOCATION_DESCRIPTOR* location,
FileWriterInterface* writer,
const std::vector<std::string>& strings) {
std::vector<RVA> rvas;
for (const std::string& string : strings) {
rvas.push_back(WriteString(writer, string));
}
location->Rva = static_cast<RVA>(writer->SeekGet());
const uint32_t string_list_entries = rvas.size();
EXPECT_TRUE(writer->Write(&string_list_entries, sizeof(string_list_entries)));
for (RVA rva : rvas) {
EXPECT_TRUE(writer->Write(&rva, sizeof(rva)));
}
location->DataSize = sizeof(string_list_entries) + rvas.size() * sizeof(RVA);
}
TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) {
StringFile string_file;
MINIDUMP_HEADER header = {};
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
MinidumpSimpleStringDictionaryEntry entry0 = {};
entry0.key = static_cast<RVA>(string_file.SeekGet());
const char kKey0[] = "the first key";
uint32_t string_size = strlen(kKey0);
EXPECT_TRUE(string_file.Write(&string_size, sizeof(string_size)));
EXPECT_TRUE(string_file.Write(kKey0, sizeof(kKey0)));
entry0.value = static_cast<RVA>(string_file.SeekGet());
const char kValue0[] = "THE FIRST VALUE EVER!";
string_size = strlen(kValue0);
EXPECT_TRUE(string_file.Write(&string_size, sizeof(string_size)));
EXPECT_TRUE(string_file.Write(kValue0, sizeof(kValue0)));
MinidumpSimpleStringDictionaryEntry entry1 = {};
entry1.key = static_cast<RVA>(string_file.SeekGet());
const char kKey1[] = "2key";
string_size = strlen(kKey1);
EXPECT_TRUE(string_file.Write(&string_size, sizeof(string_size)));
EXPECT_TRUE(string_file.Write(kKey1, sizeof(kKey1)));
entry1.value = static_cast<RVA>(string_file.SeekGet());
const char kValue1[] = "a lowly second value";
string_size = strlen(kValue1);
EXPECT_TRUE(string_file.Write(&string_size, sizeof(string_size)));
EXPECT_TRUE(string_file.Write(kValue1, sizeof(kValue1)));
MinidumpCrashpadInfo crashpad_info = {};
crashpad_info.version = MinidumpCrashpadInfo::kVersion;
crashpad_info.simple_annotations.Rva =
static_cast<RVA>(string_file.SeekGet());
uint32_t simple_string_dictionary_entries = 2;
EXPECT_TRUE(string_file.Write(&simple_string_dictionary_entries,
sizeof(simple_string_dictionary_entries)));
EXPECT_TRUE(string_file.Write(&entry0, sizeof(entry0)));
EXPECT_TRUE(string_file.Write(&entry1, sizeof(entry1)));
crashpad_info.simple_annotations.DataSize =
simple_string_dictionary_entries *
sizeof(MinidumpSimpleStringDictionaryEntry);
std::map<std::string, std::string> dictionary;
dictionary["the first key"] = "THE FIRST VALUE EVER!";
dictionary["2key"] = "a lowly second value";
WriteMinidumpSimpleStringDictionary(
&crashpad_info.simple_annotations, &string_file, dictionary);
MINIDUMP_DIRECTORY crashpad_info_directory = {};
crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
@ -124,15 +158,126 @@ TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) {
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
const auto annotations_simple_map = process_snapshot.AnnotationsSimpleMap();
EXPECT_EQ(2u, annotations_simple_map.size());
EXPECT_EQ(dictionary, annotations_simple_map);
}
auto it = annotations_simple_map.find(kKey0);
ASSERT_NE(it, annotations_simple_map.end());
EXPECT_EQ(kValue0, it->second);
TEST(ProcessSnapshotMinidump, Modules) {
StringFile string_file;
it = annotations_simple_map.find(kKey1);
ASSERT_NE(it, annotations_simple_map.end());
EXPECT_EQ(kValue1, it->second);
MINIDUMP_HEADER header = {};
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
MINIDUMP_MODULE minidump_module = {};
uint32_t minidump_module_count = 3;
MINIDUMP_DIRECTORY minidump_module_list_directory = {};
minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList;
minidump_module_list_directory.Location.DataSize =
sizeof(MINIDUMP_MODULE_LIST) +
minidump_module_count * sizeof(MINIDUMP_MODULE);
minidump_module_list_directory.Location.Rva =
static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(
string_file.Write(&minidump_module_count, sizeof(minidump_module_count)));
for (uint32_t minidump_module_index = 0;
minidump_module_index < minidump_module_count;
++minidump_module_index) {
EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module)));
}
MinidumpModuleCrashpadInfo crashpad_module_0 = {};
crashpad_module_0.version = MinidumpModuleCrashpadInfo::kVersion;
std::map<std::string, std::string> dictionary_0;
dictionary_0["ptype"] = "browser";
dictionary_0["pid"] = "12345";
WriteMinidumpSimpleStringDictionary(
&crashpad_module_0.simple_annotations, &string_file, dictionary_0);
MinidumpModuleCrashpadInfoLink crashpad_module_0_link = {};
crashpad_module_0_link.minidump_module_list_index = 0;
crashpad_module_0_link.location.DataSize = sizeof(crashpad_module_0);
crashpad_module_0_link.location.Rva = static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&crashpad_module_0, sizeof(crashpad_module_0)));
MinidumpModuleCrashpadInfo crashpad_module_2 = {};
crashpad_module_2.version = MinidumpModuleCrashpadInfo::kVersion;
std::map<std::string, std::string> dictionary_2;
dictionary_2["fakemodule"] = "yes";
WriteMinidumpSimpleStringDictionary(
&crashpad_module_2.simple_annotations, &string_file, dictionary_2);
std::vector<std::string> list_annotations_2;
list_annotations_2.push_back("first string");
list_annotations_2.push_back("last string");
WriteMinidumpStringList(
&crashpad_module_2.list_annotations, &string_file, list_annotations_2);
MinidumpModuleCrashpadInfoLink crashpad_module_2_link = {};
crashpad_module_2_link.minidump_module_list_index = 2;
crashpad_module_2_link.location.DataSize = sizeof(crashpad_module_2);
crashpad_module_2_link.location.Rva = static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&crashpad_module_2, sizeof(crashpad_module_2)));
MinidumpCrashpadInfo crashpad_info = {};
crashpad_info.version = MinidumpCrashpadInfo::kVersion;
uint32_t crashpad_module_count = 2;
crashpad_info.module_list.DataSize =
sizeof(MinidumpModuleCrashpadInfoList) +
crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink);
crashpad_info.module_list.Rva = static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(
string_file.Write(&crashpad_module_count, sizeof(crashpad_module_count)));
EXPECT_TRUE(string_file.Write(&crashpad_module_0_link,
sizeof(crashpad_module_0_link)));
EXPECT_TRUE(string_file.Write(&crashpad_module_2_link,
sizeof(crashpad_module_2_link)));
MINIDUMP_DIRECTORY crashpad_info_directory = {};
crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);
crashpad_info_directory.Location.Rva =
static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));
header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&minidump_module_list_directory,
sizeof(minidump_module_list_directory)));
EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
sizeof(crashpad_info_directory)));
header.Signature = MINIDUMP_SIGNATURE;
header.Version = MINIDUMP_VERSION;
header.NumberOfStreams = 2;
EXPECT_TRUE(string_file.SeekSet(0));
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
ProcessSnapshotMinidump process_snapshot;
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
std::vector<const ModuleSnapshot*> modules = process_snapshot.Modules();
ASSERT_EQ(minidump_module_count, modules.size());
auto annotations_simple_map = modules[0]->AnnotationsSimpleMap();
EXPECT_EQ(dictionary_0, annotations_simple_map);
auto annotations_vector = modules[0]->AnnotationsVector();
EXPECT_TRUE(annotations_vector.empty());
annotations_simple_map = modules[1]->AnnotationsSimpleMap();
EXPECT_TRUE(annotations_simple_map.empty());
annotations_vector = modules[1]->AnnotationsVector();
EXPECT_TRUE(annotations_vector.empty());
annotations_simple_map = modules[2]->AnnotationsSimpleMap();
EXPECT_EQ(dictionary_2, annotations_simple_map);
annotations_vector = modules[2]->AnnotationsVector();
EXPECT_EQ(list_annotations_2, annotations_vector);
}
} // namespace

View File

@ -69,8 +69,12 @@
'mac/thread_snapshot_mac.h',
'minidump/minidump_simple_string_dictionary_reader.cc',
'minidump/minidump_simple_string_dictionary_reader.h',
'minidump/minidump_string_list_reader.cc',
'minidump/minidump_string_list_reader.h',
'minidump/minidump_string_reader.cc',
'minidump/minidump_string_reader.h',
'minidump/module_snapshot_minidump.cc',
'minidump/module_snapshot_minidump.h',
'minidump/process_snapshot_minidump.cc',
'minidump/process_snapshot_minidump.h',
'memory_snapshot.h',