Add the minidump extension and writer for Annotation objects.

This adds extensions for MinidumpAnnotation and MinidumpAnnotationList
as well as their writer classes. Nothing currently connects the client-
side annotations to the writer, so annotations are not yet written into
minidumps.

Bug: crashpad:192
Change-Id: Ic51536157177921640ca15ae14e5e01ca875ae12
Reviewed-on: https://chromium-review.googlesource.com/731309
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Robert Sesek 2017-11-01 16:22:12 -04:00 коммит произвёл Commit Bot
Родитель 6950a552bf
Коммит 3231a80e8b
8 изменённых файлов: 514 добавлений и 4 удалений

Просмотреть файл

@ -33,6 +33,8 @@
'..',
],
'sources': [
'minidump_annotation_writer.cc',
'minidump_annotation_writer.h',
'minidump_byte_array_writer.cc',
'minidump_byte_array_writer.h',
'minidump_context.h',

Просмотреть файл

@ -0,0 +1,162 @@
// Copyright 2017 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_annotation_writer.h"
#include <memory>
#include "base/logging.h"
#include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h"
namespace crashpad {
MinidumpAnnotationWriter::MinidumpAnnotationWriter() = default;
MinidumpAnnotationWriter::~MinidumpAnnotationWriter() = default;
void MinidumpAnnotationWriter::InitializeFromSnapshot(
const AnnotationSnapshot& snapshot) {
DCHECK_EQ(state(), kStateMutable);
name_.SetUTF8(snapshot.name);
annotation_.type = snapshot.type;
annotation_.reserved = 0;
value_.set_data(snapshot.value);
}
void MinidumpAnnotationWriter::InitializeWithData(
const std::string& name,
uint16_t type,
const std::vector<uint8_t>& data) {
DCHECK_EQ(state(), kStateMutable);
name_.SetUTF8(name);
annotation_.type = type;
annotation_.reserved = 0;
value_.set_data(data);
}
bool MinidumpAnnotationWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
if (!MinidumpWritable::Freeze()) {
return false;
}
name_.RegisterRVA(&annotation_.name);
value_.RegisterRVA(&annotation_.value);
return true;
}
size_t MinidumpAnnotationWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
// This object is written by the MinidumpAnnotationListWriter, and its
// children write themselves.
return 0;
}
std::vector<internal::MinidumpWritable*> MinidumpAnnotationWriter::Children() {
DCHECK_GE(state(), kStateFrozen);
return {&name_, &value_};
}
bool MinidumpAnnotationWriter::WriteObject(FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
// This object is written by the MinidumpAnnotationListWriter, and its
// children write themselves.
return true;
}
MinidumpAnnotationListWriter::MinidumpAnnotationListWriter()
: minidump_list_(new MinidumpAnnotationList()) {}
MinidumpAnnotationListWriter::~MinidumpAnnotationListWriter() = default;
void MinidumpAnnotationListWriter::InitializeFromList(
const std::vector<AnnotationSnapshot>& list) {
DCHECK_EQ(state(), kStateMutable);
for (const auto& annotation : list) {
auto writer = std::make_unique<MinidumpAnnotationWriter>();
writer->InitializeFromSnapshot(annotation);
AddObject(std::move(writer));
}
}
void MinidumpAnnotationListWriter::AddObject(
std::unique_ptr<MinidumpAnnotationWriter> annotation_writer) {
DCHECK_EQ(state(), kStateMutable);
objects_.push_back(std::move(annotation_writer));
}
bool MinidumpAnnotationListWriter::IsUseful() const {
return !objects_.empty();
}
bool MinidumpAnnotationListWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
if (!MinidumpWritable::Freeze()) {
return false;
}
if (!AssignIfInRange(&minidump_list_->count, objects_.size())) {
LOG(ERROR) << "annotation list size " << objects_.size()
<< " is out of range";
return false;
}
return true;
}
size_t MinidumpAnnotationListWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
return sizeof(*minidump_list_) + sizeof(MinidumpAnnotation) * objects_.size();
}
std::vector<internal::MinidumpWritable*>
MinidumpAnnotationListWriter::Children() {
DCHECK_GE(state(), kStateFrozen);
std::vector<internal::MinidumpWritable*> children(objects_.size());
for (size_t i = 0; i < objects_.size(); ++i) {
children[i] = objects_[i].get();
}
return children;
}
bool MinidumpAnnotationListWriter::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
std::vector<WritableIoVec> iov(1 + objects_.size());
iov[0].iov_base = minidump_list_.get();
iov[0].iov_len = sizeof(*minidump_list_);
for (const auto& object : objects_) {
iov.emplace_back(WritableIoVec{object->minidump_annotation(),
sizeof(MinidumpAnnotation)});
}
return file_writer->WriteIoVec(&iov);
}
} // namespace crashpad

Просмотреть файл

@ -0,0 +1,109 @@
// Copyright 2017 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_ANNOTATION_WRITER_H_
#define CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_
#include <memory>
#include <vector>
#include "minidump/minidump_byte_array_writer.h"
#include "minidump/minidump_extensions.h"
#include "minidump/minidump_string_writer.h"
#include "minidump/minidump_writable.h"
#include "snapshot/annotation_snapshot.h"
namespace crashpad {
//! \brief The writer for a MinidumpAnnotation object in a minidump file.
//!
//! Because MinidumpAnnotation objects only appear as elements
//! of MinidumpAnnotationList objects, this class does not write any
//! data on its own. It makes its MinidumpAnnotation data available to its
//! MinidumpAnnotationList parent, which writes it as part of a
//! MinidumpAnnotationList.
class MinidumpAnnotationWriter final : public internal::MinidumpWritable {
public:
MinidumpAnnotationWriter();
~MinidumpAnnotationWriter();
//! \brief Initializes the annotation writer with data from an
//! AnnotationSnapshot.
void InitializeFromSnapshot(const AnnotationSnapshot& snapshot);
//! \brief Initializes the annotation writer with data values.
void InitializeWithData(const std::string& name,
uint16_t type,
const std::vector<uint8_t>& data);
//! \brief Returns the MinidumpAnnotation referencing this objects data.
const MinidumpAnnotation* minidump_annotation() const { return &annotation_; }
protected:
// MinidumpWritable:
bool Freeze() override;
size_t SizeOfObject() override;
std::vector<internal::MinidumpWritable*> Children() override;
bool WriteObject(FileWriterInterface* file_writer) override;
private:
MinidumpAnnotation annotation_;
internal::MinidumpUTF8StringWriter name_;
MinidumpByteArrayWriter value_;
DISALLOW_COPY_AND_ASSIGN(MinidumpAnnotationWriter);
};
//! \brief The writer for a MinidumpAnnotationList object in a minidump file,
//! containing a list of MinidumpAnnotation objects.
class MinidumpAnnotationListWriter final : public internal::MinidumpWritable {
public:
MinidumpAnnotationListWriter();
~MinidumpAnnotationListWriter();
//! \brief Initializes the annotation list writer with a list of
//! AnnotationSnapshot objects.
void InitializeFromList(const std::vector<AnnotationSnapshot>& list);
//! \brief Adds a single MinidumpAnnotationWriter to the list to be written.
void AddObject(std::unique_ptr<MinidumpAnnotationWriter> annotation_writer);
//! \brief Determines whether the object is useful.
//!
//! A useful object is one that carries data that makes a meaningful
//! contribution to a minidump file. An object carrying entries would be
//! considered useful.
//!
//! \return `true` if the object is useful, `false` otherwise.
bool IsUseful() const;
protected:
// MinidumpWritable:
bool Freeze() override;
size_t SizeOfObject() override;
std::vector<internal::MinidumpWritable*> Children() override;
bool WriteObject(FileWriterInterface* file_writer) override;
private:
std::unique_ptr<MinidumpAnnotationList> minidump_list_;
std::vector<std::unique_ptr<MinidumpAnnotationWriter>> objects_;
DISALLOW_COPY_AND_ASSIGN(MinidumpAnnotationListWriter);
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_

Просмотреть файл

@ -0,0 +1,188 @@
// Copyright 2017 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_annotation_writer.h"
#include <memory>
#include "base/macros.h"
#include "gtest/gtest.h"
#include "minidump/minidump_extensions.h"
#include "minidump/test/minidump_byte_array_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 {
const MinidumpAnnotationList* MinidumpAnnotationListAtStart(
const std::string& file_contents,
uint32_t count) {
MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;
location_descriptor.DataSize = static_cast<uint32_t>(
sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation));
location_descriptor.Rva = 0;
return MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(
file_contents, location_descriptor);
}
TEST(MinidumpAnnotationWriter, EmptyList) {
StringFile string_file;
MinidumpAnnotationListWriter list_writer;
EXPECT_FALSE(list_writer.IsUseful());
EXPECT_TRUE(list_writer.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), sizeof(MinidumpAnnotationList));
auto* list = MinidumpAnnotationListAtStart(string_file.string(), 0);
ASSERT_TRUE(list);
EXPECT_EQ(0u, list->count);
}
TEST(MinidumpAnnotationWriter, OneItem) {
StringFile string_file;
const char kName[] = "name";
const uint16_t kType = 0xFFFF;
const std::vector<uint8_t> kValue{'v', 'a', 'l', 'u', 'e', '\0'};
auto annotation_writer = std::make_unique<MinidumpAnnotationWriter>();
annotation_writer->InitializeWithData(kName, kType, kValue);
MinidumpAnnotationListWriter list_writer;
list_writer.AddObject(std::move(annotation_writer));
EXPECT_TRUE(list_writer.IsUseful());
EXPECT_TRUE(list_writer.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(),
sizeof(MinidumpAnnotationList) + sizeof(MinidumpAnnotation) +
sizeof(MinidumpUTF8String) + sizeof(kName) +
sizeof(MinidumpByteArray) + kValue.size() +
3); // 3 for padding.
auto* list = MinidumpAnnotationListAtStart(string_file.string(), 1);
ASSERT_TRUE(list);
EXPECT_EQ(1u, list->count);
EXPECT_EQ(kName,
MinidumpUTF8StringAtRVAAsString(string_file.string(),
list->objects[0].name));
EXPECT_EQ(kType, list->objects[0].type);
EXPECT_EQ(0u, list->objects[0].reserved);
EXPECT_EQ(
kValue,
MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value));
}
TEST(MinidumpAnnotationWriter, ThreeItems) {
StringFile string_file;
const char* kNames[] = {
"~~FIRST~~", " second + ", "3",
};
const uint16_t kTypes[] = {
0x1, 0xABCD, 0x42,
};
const std::vector<uint8_t> kValues[] = {
{'\0'}, {0xB0, 0xA0, 0xD0, 0xD0, 0xD0}, {'T'},
};
MinidumpAnnotationListWriter list_writer;
for (size_t i = 0; i < arraysize(kNames); ++i) {
auto annotation = std::make_unique<MinidumpAnnotationWriter>();
annotation->InitializeWithData(kNames[i], kTypes[i], kValues[i]);
list_writer.AddObject(std::move(annotation));
}
EXPECT_TRUE(list_writer.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(),
sizeof(MinidumpAnnotationList) + 3 * sizeof(MinidumpAnnotation) +
3 * sizeof(MinidumpUTF8String) + 3 * sizeof(MinidumpByteArray) +
strlen(kNames[0]) + 1 + kValues[0].size() + 2 +
strlen(kNames[1]) + 1 + 3 + kValues[1].size() + 1 +
strlen(kNames[2]) + 1 + 3 + kValues[2].size() + 2);
auto* list = MinidumpAnnotationListAtStart(string_file.string(), 3);
ASSERT_TRUE(list);
EXPECT_EQ(3u, list->count);
for (size_t i = 0; i < 3; ++i) {
EXPECT_EQ(kNames[i],
MinidumpUTF8StringAtRVAAsString(string_file.string(),
list->objects[i].name));
EXPECT_EQ(kTypes[i], list->objects[i].type);
EXPECT_EQ(0u, list->objects[i].reserved);
EXPECT_EQ(
kValues[i],
MinidumpByteArrayAtRVA(string_file.string(), list->objects[i].value));
}
}
TEST(MinidumpAnnotationWriter, DuplicateNames) {
StringFile string_file;
const char kName[] = "@@name!";
const uint16_t kType = 0x1;
const std::vector<uint8_t> kValue1{'r', 'e', 'd', '\0'};
const std::vector<uint8_t> kValue2{'m', 'a', 'g', 'e', 'n', 't', 'a', '\0'};
MinidumpAnnotationListWriter list_writer;
auto annotation = std::make_unique<MinidumpAnnotationWriter>();
annotation->InitializeWithData(kName, kType, kValue1);
list_writer.AddObject(std::move(annotation));
annotation = std::make_unique<MinidumpAnnotationWriter>();
annotation->InitializeWithData(kName, kType, kValue2);
list_writer.AddObject(std::move(annotation));
EXPECT_TRUE(list_writer.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(),
sizeof(MinidumpAnnotationList) + 2 * sizeof(MinidumpAnnotation) +
2 * sizeof(MinidumpUTF8String) + 2 * sizeof(MinidumpByteArray) +
2 * sizeof(kName) + kValue1.size() + kValue2.size());
auto* list = MinidumpAnnotationListAtStart(string_file.string(), 2);
ASSERT_TRUE(list);
EXPECT_EQ(2u, list->count);
EXPECT_EQ(kName,
MinidumpUTF8StringAtRVAAsString(string_file.string(),
list->objects[0].name));
EXPECT_EQ(kType, list->objects[0].type);
EXPECT_EQ(
kValue1,
MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value));
EXPECT_EQ(kName,
MinidumpUTF8StringAtRVAAsString(string_file.string(),
list->objects[1].name));
EXPECT_EQ(kType, list->objects[1].type);
EXPECT_EQ(
kValue2,
MinidumpByteArrayAtRVA(string_file.string(), list->objects[1].value));
}
} // namespace
} // namespace test
} // namespace crashpad

Просмотреть файл

@ -282,6 +282,32 @@ struct ALIGNAS(4) PACKED MinidumpSimpleStringDictionary {
MinidumpSimpleStringDictionaryEntry entries[0];
};
//! \brief A typed annotation object.
struct ALIGNAS(4) PACKED MinidumpAnnotation {
//! \brief ::RVA of a MinidumpUTF8String containing the name of the
//! annotation.
RVA name;
//! \brief The type of data stored in the \a value of the annotation. This
//! may correspond to an \a Annotation::Type or it may be user-defined.
uint16_t type;
//! \brief This field is always `0`.
uint16_t reserved;
//! \brief ::RVA of a MinidumpByteArray to the data for the annotation.
RVA value;
};
//! \brief A list of annotation objects.
struct ALIGNAS(4) PACKED MinidumpAnnotationList {
//! \brief The number of annotation objects present.
uint32_t count;
//! \brief A list of MinidumpAnnotation objects.
MinidumpAnnotation objects[0];
};
//! \brief Additional Crashpad-specific information about a module carried
//! within a minidump file.
//!

Просмотреть файл

@ -64,6 +64,7 @@
'..',
],
'sources': [
'minidump_annotation_writer_test.cc',
'minidump_byte_array_writer_test.cc',
'minidump_context_writer_test.cc',
'minidump_crashpad_info_writer_test.cc',

Просмотреть файл

@ -223,6 +223,12 @@ struct MinidumpSimpleStringDictionaryListTraits {
}
};
struct MinidumpAnnotationListObjectsTraits {
using ListType = MinidumpAnnotationList;
enum : size_t { kElementSize = sizeof(MinidumpAnnotation) };
static size_t ElementCount(const ListType* list) { return list->count; }
};
template <typename T>
const typename T::ListType* MinidumpListAtLocationDescriptor(
const std::string& file_contents,
@ -313,6 +319,15 @@ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
MinidumpSimpleStringDictionaryListTraits>(file_contents, location);
}
template <>
const MinidumpAnnotationList*
MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpAnnotationListObjectsTraits>(
file_contents, location);
}
namespace {
template <typename T>

Просмотреть файл

@ -96,6 +96,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpAnnotationList);
// These types have final fields carrying variable-sized data (typically string
// data).
@ -141,10 +142,10 @@ const T* TMinidumpWritableAtLocationDescriptor(
//! - With a MINIDUMP_HEADER template parameter, a template specialization
//! ensures that the structures magic number and version fields are correct.
//! - With a MINIDUMP_MEMORY_LIST, MINIDUMP_THREAD_LIST, MINIDUMP_MODULE_LIST,
//! MINIDUMP_MEMORY_INFO_LIST, or MinidumpSimpleStringDictionary template
//! parameter, template specializations ensure that the size given by \a
//! location matches the size expected of a stream containing the number of
//! elements it claims to have.
//! MINIDUMP_MEMORY_INFO_LIST, MinidumpSimpleStringDictionary, or
//! MinidumpAnnotationList template parameter, template specializations
//! ensure that the size given by \a location matches the size expected of a
//! stream containing the number of elements it claims to have.
//! - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70
//! template parameter, template specializations ensure that the structure
//! has the expected format including any magic number and the `NUL`-
@ -230,6 +231,12 @@ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MinidumpAnnotationList*
MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location);
//! \brief Returns a typed minidump object located within a minidump files
//! contents, where the offset of the object is known.
//!