Add ProcessSnapshotMinidump, the beginning of the minidump variant of

the Snapshot family.

For the time being, only ProcessSnapshotMinidump::AnnotationsSimpleMap()
is implemented. In order to complete the initial uploader for Crashpad,
ModuleSnapshotMinidump::AnnotationsSimpleMap() is also needed, to be
accessed by ProcessSnapshotMinidump::Modules().

TEST=snapshot_test ProcessSnapshotMinidump.*
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/932153003
This commit is contained in:
Mark Mentovai 2015-02-26 14:43:10 -05:00
parent 583314184a
commit 40b931bd8e
9 changed files with 645 additions and 0 deletions

View File

@ -107,6 +107,7 @@ void ProcessSnapshotMac::ProcessCPUTimes(timeval* user_time,
const std::map<std::string, std::string>&
ProcessSnapshotMac::AnnotationsSimpleMap() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return annotations_simple_map_;
}

View File

@ -0,0 +1,89 @@
// 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_simple_string_dictionary_reader.h"
#include <vector>
#include <utility>
#include "base/logging.h"
#include "minidump/minidump_extensions.h"
#include "snapshot/minidump/minidump_string_reader.h"
namespace crashpad {
namespace internal {
bool ReadMinidumpSimpleStringDictionary(
FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR& location,
std::map<std::string, std::string>* dictionary) {
if (location.Rva == 0) {
dictionary->clear();
return true;
}
if (location.DataSize < sizeof(MinidumpSimpleStringDictionary)) {
LOG(ERROR) << "simple_string_dictionary 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 !=
entry_count * sizeof(MinidumpSimpleStringDictionaryEntry)) {
LOG(ERROR) << "simple_string_dictionary size mismatch";
return false;
}
std::vector<MinidumpSimpleStringDictionaryEntry> entries(entry_count);
if (!file_reader->ReadExactly(&entries[0],
entry_count * sizeof(entries[0]))) {
return false;
}
std::map<std::string, std::string> local_dictionary;
for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {
std::string key;
if (!ReadMinidumpUTF8String(file_reader, entry.key, &key)) {
// Not a hard error, keep trying.
continue;
}
std::string value;
if (!ReadMinidumpUTF8String(file_reader, entry.value, &value)) {
// Not a hard error, keep trying.
continue;
}
if (local_dictionary.find(key) != local_dictionary.end()) {
LOG(WARNING) << "duplicate key " << key << ", discarding value " << value;
continue;
}
local_dictionary.insert(std::make_pair(key, value));
}
dictionary->swap(local_dictionary);
return true;
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,44 @@
// 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_SIMPLE_STRING_DICTIONARY_READER_H_
#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_
#include <windows.h>
#include <dbghelp.h>
#include <map>
#include <string>
#include "util/file/file_reader.h"
namespace crashpad {
namespace internal {
//! \brief Reads a MinidumpSimpleStringDictionary from a minidump file \a
//! 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.
bool ReadMinidumpSimpleStringDictionary(
FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR& location,
std::map<std::string, std::string>* dictionary);
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_

View File

@ -0,0 +1,50 @@
// 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_reader.h"
#include "base/logging.h"
#include "minidump/minidump_extensions.h"
namespace crashpad {
namespace internal {
bool ReadMinidumpUTF8String(FileReaderInterface* file_reader,
RVA rva,
std::string* string) {
if (rva == 0) {
string->clear();
return true;
}
if (!file_reader->SeekSet(rva)) {
return false;
}
uint32_t string_size;
if (!file_reader->ReadExactly(&string_size, sizeof(string_size))) {
return false;
}
std::string local_string(string_size, '\0');
if (!file_reader->ReadExactly(&local_string[0], string_size)) {
return false;
}
string->swap(local_string);
return true;
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,40 @@
// 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_READER_H_
#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_
#include <windows.h>
#include <dbghelp.h>
#include <string>
#include "util/file/file_reader.h"
namespace crashpad {
namespace internal {
//! \brief Reads a MinidumpUTF8String from a minidump file at offset \a rva in
//! \a file_reader, and returns it in \a string.
//!
//! \return `true` on success, with \a string set. `false` on failure, with a
//! message logged.
bool ReadMinidumpUTF8String(FileReaderInterface* file_reader,
RVA rva,
std::string* string);
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_

View File

@ -0,0 +1,189 @@
// 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/process_snapshot_minidump.h"
#include "util/file/file_io.h"
#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
namespace crashpad {
ProcessSnapshotMinidump::ProcessSnapshotMinidump()
: ProcessSnapshot(),
header_(),
stream_directory_(),
stream_map_(),
crashpad_info_(),
annotations_simple_map_(),
file_reader_(nullptr),
initialized_() {
}
ProcessSnapshotMinidump::~ProcessSnapshotMinidump() {
}
bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
file_reader_ = file_reader;
if (!file_reader_->SeekSet(0)) {
return false;
}
if (!file_reader_->ReadExactly(&header_, sizeof(header_))) {
return false;
}
if (header_.Signature != MINIDUMP_SIGNATURE) {
LOG(ERROR) << "minidump signature mismatch";
return false;
}
if (header_.Version != MINIDUMP_VERSION) {
LOG(ERROR) << "minidump version mismatch";
return false;
}
if (!file_reader->SeekSet(header_.StreamDirectoryRva)) {
return false;
}
stream_directory_.resize(header_.NumberOfStreams);
if (!file_reader_->ReadExactly(
&stream_directory_[0],
header_.NumberOfStreams * sizeof(stream_directory_[0]))) {
return false;
}
for (const MINIDUMP_DIRECTORY& directory : stream_directory_) {
const MinidumpStreamType stream_type =
static_cast<MinidumpStreamType>(directory.StreamType);
if (stream_map_.find(stream_type) != stream_map_.end()) {
LOG(ERROR) << "duplicate streams for type " << directory.StreamType;
return false;
}
stream_map_[stream_type] = &directory.Location;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
InitializeCrashpadInfo();
return true;
}
pid_t ProcessSnapshotMinidump::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
return 0;
}
pid_t ProcessSnapshotMinidump::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
return 0;
}
void ProcessSnapshotMinidump::SnapshotTime(timeval* snapshot_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
snapshot_time->tv_sec = 0;
snapshot_time->tv_usec = 0;
}
void ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
start_time->tv_sec = 0;
start_time->tv_usec = 0;
}
void ProcessSnapshotMinidump::ProcessCPUTimes(timeval* user_time,
timeval* system_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
user_time->tv_sec = 0;
user_time->tv_usec = 0;
system_time->tv_sec = 0;
system_time->tv_usec = 0;
}
const std::map<std::string, std::string>&
ProcessSnapshotMinidump::AnnotationsSimpleMap() const {
// TODO(mark): This method should not be const, although the interface
// currently imposes this requirement. Making it non-const would allow
// 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().
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return annotations_simple_map_;
}
const SystemSnapshot* ProcessSnapshotMinidump::System() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
return nullptr;
}
std::vector<const ThreadSnapshot*> ProcessSnapshotMinidump::Threads() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
return std::vector<const ThreadSnapshot*>();
}
std::vector<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
return std::vector<const ModuleSnapshot*>();
}
const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED();
return nullptr;
}
void ProcessSnapshotMinidump::InitializeCrashpadInfo() {
const auto& it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo);
if (it == stream_map_.end()) {
return;
}
if (it->second->DataSize < sizeof(crashpad_info_)) {
LOG(ERROR) << "crashpad_info size mismatch";
return;
}
if (!file_reader_->SeekSet(it->second->Rva)) {
return;
}
if (!file_reader_->ReadExactly(&crashpad_info_, sizeof(crashpad_info_))) {
return;
}
if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {
LOG(ERROR) << "crashpad_info version mismatch";
return;
}
internal::ReadMinidumpSimpleStringDictionary(
file_reader_,
crashpad_info_.simple_annotations,
&annotations_simple_map_);
}
} // namespace crashpad

View File

@ -0,0 +1,85 @@
// 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_PROCESS_SNAPSHOT_MINIDUMP_H_
#define CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_
#include <sys/time.h>
#include <windows.h>
#include <dbghelp.h>
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "minidump/minidump_extensions.h"
#include "snapshot/exception_snapshot.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"
namespace crashpad {
//! \brief A ProcessSnapshot based on a minidump file.
class ProcessSnapshotMinidump final : public ProcessSnapshot {
public:
ProcessSnapshotMinidump();
~ProcessSnapshotMinidump() override;
//! \brief Initializes the object.
//!
//! \param[in] file_reader A file reader corresponding to a minidump file.
//! The file reader must support seeking.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool Initialize(FileReaderInterface* file_reader);
// ProcessSnapshot:
pid_t ProcessID() const override;
pid_t ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
const std::map<std::string, std::string>& AnnotationsSimpleMap()
const override;
const SystemSnapshot* System() const override;
std::vector<const ThreadSnapshot*> Threads() const override;
std::vector<const ModuleSnapshot*> Modules() const override;
const ExceptionSnapshot* Exception() const override;
private:
// Initializes data carried in a MinidumpCrashpadInfo stream on behalf of
// Initialize().
void InitializeCrashpadInfo();
MINIDUMP_HEADER header_;
std::vector<MINIDUMP_DIRECTORY> stream_directory_;
std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
MinidumpCrashpadInfo crashpad_info_;
std::map<std::string, std::string> annotations_simple_map_;
FileReaderInterface* file_reader_; // weak
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMinidump);
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_

View File

@ -0,0 +1,140 @@
// 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/process_snapshot_minidump.h"
#include <string.h>
#include <windows.h>
#include <dbghelp.h>
#include "base/memory/scoped_ptr.h"
#include "gtest/gtest.h"
#include "util/file/string_file.h"
namespace crashpad {
namespace test {
namespace {
TEST(ProcessSnapshotMinidump, EmptyFile) {
StringFile string_file;
ProcessSnapshotMinidump process_snapshot;
EXPECT_FALSE(process_snapshot.Initialize(&string_file));
}
TEST(ProcessSnapshotMinidump, InvalidSignatureAndVersion) {
StringFile string_file;
MINIDUMP_HEADER header = {};
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
ProcessSnapshotMinidump process_snapshot;
EXPECT_FALSE(process_snapshot.Initialize(&string_file));
}
TEST(ProcessSnapshotMinidump, Empty) {
StringFile string_file;
MINIDUMP_HEADER header = {};
header.Signature = MINIDUMP_SIGNATURE;
header.Version = MINIDUMP_VERSION;
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
ProcessSnapshotMinidump process_snapshot;
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
}
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);
MINIDUMP_DIRECTORY crashpad_info_directory = {};
crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
crashpad_info_directory.Location.Rva =
static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));
crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);
header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
sizeof(crashpad_info_directory)));
header.Signature = MINIDUMP_SIGNATURE;
header.Version = MINIDUMP_VERSION;
header.NumberOfStreams = 1;
EXPECT_TRUE(string_file.SeekSet(0));
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
ProcessSnapshotMinidump process_snapshot;
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
const auto annotations_simple_map = process_snapshot.AnnotationsSimpleMap();
EXPECT_EQ(2u, annotations_simple_map.size());
auto it = annotations_simple_map.find(kKey0);
ASSERT_NE(it, annotations_simple_map.end());
EXPECT_EQ(kValue0, it->second);
it = annotations_simple_map.find(kKey1);
ASSERT_NE(it, annotations_simple_map.end());
EXPECT_EQ(kValue1, it->second);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -67,6 +67,12 @@
'mac/system_snapshot_mac.h',
'mac/thread_snapshot_mac.cc',
'mac/thread_snapshot_mac.h',
'minidump/minidump_simple_string_dictionary_reader.cc',
'minidump/minidump_simple_string_dictionary_reader.h',
'minidump/minidump_string_reader.cc',
'minidump/minidump_string_reader.h',
'minidump/process_snapshot_minidump.cc',
'minidump/process_snapshot_minidump.h',
'memory_snapshot.h',
'module_snapshot.h',
'process_snapshot.h',
@ -128,6 +134,7 @@
'mac/process_reader_test.cc',
'mac/process_types_test.cc',
'mac/system_snapshot_mac_test.cc',
'minidump/process_snapshot_minidump_test.cc',
],
},
],