Add AnnotationReader to read annotation types via a ProcessMemory

Bug: crashpad:30
Change-Id: Icd13d29992b8684ca92916068f12428c25e0e775
Reviewed-on: https://chromium-review.googlesource.com/846519
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2018-01-09 10:22:24 -08:00 committed by Commit Bot
parent 70563e92f4
commit 878af9cbbd
6 changed files with 399 additions and 0 deletions

View File

@ -102,6 +102,8 @@ static_library("snapshot") {
sources += [
"crashpad_types/crashpad_info_reader.cc",
"crashpad_types/crashpad_info_reader.h",
"crashpad_types/image_annotation_reader.cc",
"crashpad_types/image_annotation_reader.h",
"elf/elf_dynamic_array_reader.cc",
"elf/elf_dynamic_array_reader.h",
"elf/elf_image_reader.cc",
@ -288,6 +290,7 @@ source_set("snapshot_test") {
if (crashpad_is_linux || crashpad_is_android) {
sources += [
"crashpad_types/crashpad_info_reader_test.cc",
"crashpad_types/image_annotation_reader_test.cc",
"elf/elf_image_reader_test.cc",
"elf/elf_image_reader_test_note.S",
"elf/test_exported_symbols.sym",

View File

@ -0,0 +1,153 @@
// 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 "snapshot/crashpad_types/image_annotation_reader.h"
#include <string.h>
#include <sys/types.h>
#include <algorithm>
#include <utility>
#include "base/logging.h"
#include "build/build_config.h"
#include "client/annotation.h"
#include "client/annotation_list.h"
#include "client/simple_string_dictionary.h"
#include "snapshot/snapshot_constants.h"
#include "util/linux/traits.h"
namespace crashpad {
namespace process_types {
template <class Traits>
struct Annotation {
typename Traits::Address link_node;
typename Traits::Address name;
typename Traits::Address value;
uint32_t size;
uint16_t type;
};
template <class Traits>
struct AnnotationList {
typename Traits::Address tail_pointer;
Annotation<Traits> head;
Annotation<Traits> tail;
};
} // namespace process_types
#if defined(ARCH_CPU_64_BITS)
#define NATIVE_TRAITS Traits64
#else
#define NATIVE_TRAITS Traits32
#endif // ARCH_CPU_64_BITS
static_assert(sizeof(process_types::Annotation<NATIVE_TRAITS>) ==
sizeof(Annotation),
"Annotation size mismatch");
static_assert(sizeof(process_types::AnnotationList<NATIVE_TRAITS>) ==
sizeof(AnnotationList),
"AnnotationList size mismatch");
#undef NATIVE_TRAITS
ImageAnnotationReader::ImageAnnotationReader(const ProcessMemoryRange* memory)
: memory_(memory) {}
ImageAnnotationReader::~ImageAnnotationReader() = default;
bool ImageAnnotationReader::SimpleMap(
VMAddress address,
std::map<std::string, std::string>* annotations) const {
std::vector<SimpleStringDictionary::Entry> simple_annotations(
SimpleStringDictionary::num_entries);
if (!memory_->Read(address,
simple_annotations.size() * sizeof(simple_annotations[0]),
&simple_annotations[0])) {
return false;
}
for (const auto& entry : simple_annotations) {
size_t key_length = strnlen(entry.key, sizeof(entry.key));
if (key_length) {
std::string key(entry.key, key_length);
std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));
if (!annotations->insert(std::make_pair(key, value)).second) {
LOG(WARNING) << "duplicate simple annotation " << key << " " << value;
}
}
}
return true;
}
bool ImageAnnotationReader::AnnotationsList(
VMAddress address,
std::vector<AnnotationSnapshot>* annotations) const {
return memory_->Is64Bit()
? ReadAnnotationList<Traits64>(address, annotations)
: ReadAnnotationList<Traits32>(address, annotations);
}
template <class Traits>
bool ImageAnnotationReader::ReadAnnotationList(
VMAddress address,
std::vector<AnnotationSnapshot>* annotations) const {
process_types::AnnotationList<Traits> annotation_list;
if (!memory_->Read(address, sizeof(annotation_list), &annotation_list)) {
LOG(ERROR) << "could not read annotation list";
return false;
}
process_types::Annotation<Traits> current = annotation_list.head;
for (size_t index = 0; current.link_node != annotation_list.tail_pointer &&
index < kMaxNumberOfAnnotations;
++index) {
if (!memory_->Read(current.link_node, sizeof(current), &current)) {
LOG(ERROR) << "could not read annotation at index " << index;
return false;
}
if (current.size == 0) {
continue;
}
AnnotationSnapshot snapshot;
snapshot.type = current.type;
if (!memory_->ReadCStringSizeLimited(
current.name, Annotation::kNameMaxLength, &snapshot.name)) {
LOG(WARNING) << "could not read annotation name at index " << index;
continue;
}
size_t value_length =
std::min(static_cast<size_t>(current.size), Annotation::kValueMaxSize);
snapshot.value.resize(value_length);
if (!memory_->Read(current.value, value_length, snapshot.value.data())) {
LOG(WARNING) << "could not read annotation value at index " << index;
continue;
}
annotations->push_back(std::move(snapshot));
}
return true;
}
} // namespace crashpad

View File

@ -0,0 +1,76 @@
// 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_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_
#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_
#include <map>
#include <string>
#include <vector>
#include "base/macros.h"
#include "snapshot/annotation_snapshot.h"
#include "util/misc/address_types.h"
#include "util/process/process_memory_range.h"
namespace crashpad {
//! \brief Reads Annotations from another process via a ProcessMemoryRange.
//!
//! These annotations are stored for the benefit of crash reporters, and provide
//! information thought to be potentially useful for crash analysis.
class ImageAnnotationReader {
public:
//! \brief Constructs the object.
//!
//! \param[in] memory A memory reader for the remote process.
explicit ImageAnnotationReader(const ProcessMemoryRange* memory);
~ImageAnnotationReader();
//! \brief Reads annotations that are organized as key-value pairs, where all
//! keys and values are strings.
//!
//! \param[in] address The address in the target process' address space of a
//! SimpleStringDictionary containing the annotations to read.
//! \param[out] annotations The annotations read, valid if this method
//! returns `true`.
//! \return `true` on success. `false` on failure with a message logged.
bool SimpleMap(VMAddress address,
std::map<std::string, std::string>* annotations) const;
//! \brief Reads the module's annotations that are organized as a list of
//! typed annotation objects.
//!
//! \param[in] address The address in the target process' address space of an
//! AnnotationList.
//! \param[out] annotations The annotations read, valid if this method returns
//! `true`.
//! \return `true` on success. `false` on failure with a message logged.
bool AnnotationsList(VMAddress,
std::vector<AnnotationSnapshot>* annotations) const;
private:
template <class Traits>
bool ReadAnnotationList(VMAddress address,
std::vector<AnnotationSnapshot>* annotations) const;
const ProcessMemoryRange* memory_; // weak
DISALLOW_COPY_AND_ASSIGN(ImageAnnotationReader);
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_

View File

@ -0,0 +1,164 @@
// 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 "snapshot/crashpad_types/image_annotation_reader.h"
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include "base/logging.h"
#include "build/build_config.h"
#include "client/annotation.h"
#include "client/annotation_list.h"
#include "client/simple_string_dictionary.h"
#include "gtest/gtest.h"
#include "test/multiprocess.h"
#include "util/file/file_io.h"
#include "util/misc/as_underlying_type.h"
#include "util/misc/from_pointer_cast.h"
#include "util/process/process_memory_linux.h"
namespace crashpad {
namespace test {
namespace {
void ExpectSimpleMap(const std::map<std::string, std::string>& map,
const SimpleStringDictionary& expected_map) {
EXPECT_EQ(map.size(), expected_map.GetCount());
for (const auto& pair : map) {
EXPECT_EQ(pair.second, expected_map.GetValueForKey(pair.first));
}
}
void ExpectAnnotationList(const std::vector<AnnotationSnapshot>& list,
AnnotationList& expected_list) {
size_t index = 0;
for (const Annotation* expected_annotation : expected_list) {
const AnnotationSnapshot& annotation = list[index++];
EXPECT_EQ(annotation.name, expected_annotation->name());
EXPECT_EQ(annotation.type, AsUnderlyingType(expected_annotation->type()));
EXPECT_EQ(annotation.value.size(), expected_annotation->size());
EXPECT_EQ(memcmp(annotation.value.data(),
expected_annotation->value(),
std::min(VMSize{annotation.value.size()},
VMSize{expected_annotation->size()})),
0);
}
}
class AnnotationTest {
public:
AnnotationTest()
: expected_simple_map_(),
test_annotations_(),
expected_annotation_list_() {
expected_simple_map_.SetKeyValue("key", "value");
expected_simple_map_.SetKeyValue("key2", "value2");
static constexpr char kAnnotationName[] = "test annotation";
static constexpr char kAnnotationValue[] = "test annotation value";
test_annotations_.push_back(std::make_unique<Annotation>(
Annotation::Type::kString,
kAnnotationName,
reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue))));
test_annotations_.back()->SetSize(sizeof(kAnnotationValue));
expected_annotation_list_.Add(test_annotations_.back().get());
static constexpr char kAnnotationName2[] = "test annotation2";
static constexpr char kAnnotationValue2[] = "test annotation value2";
test_annotations_.push_back(std::make_unique<Annotation>(
Annotation::Type::kString,
kAnnotationName2,
reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue2))));
test_annotations_.back()->SetSize(sizeof(kAnnotationValue2));
expected_annotation_list_.Add(test_annotations_.back().get());
}
~AnnotationTest() = default;
void ExpectAnnotations(pid_t pid, bool is_64_bit) {
ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(pid));
ProcessMemoryRange range;
ASSERT_TRUE(range.Initialize(&memory, is_64_bit));
ImageAnnotationReader reader(&range);
std::map<std::string, std::string> simple_map;
ASSERT_TRUE(reader.SimpleMap(
FromPointerCast<VMAddress>(&expected_simple_map_), &simple_map));
ExpectSimpleMap(simple_map, expected_simple_map_);
std::vector<AnnotationSnapshot> annotation_list;
ASSERT_TRUE(reader.AnnotationsList(
FromPointerCast<VMAddress>(&expected_annotation_list_),
&annotation_list));
ExpectAnnotationList(annotation_list, expected_annotation_list_);
}
private:
SimpleStringDictionary expected_simple_map_;
std::vector<std::unique_ptr<Annotation>> test_annotations_;
AnnotationList expected_annotation_list_;
DISALLOW_COPY_AND_ASSIGN(AnnotationTest);
};
TEST(ImageAnnotationReader, ReadFromSelf) {
AnnotationTest test;
#if defined(ARCH_CPU_64_BITS)
constexpr bool am_64_bit = true;
#else
constexpr bool am_64_bit = false;
#endif
test.ExpectAnnotations(getpid(), am_64_bit);
}
class ReadFromChildTest : public Multiprocess {
public:
ReadFromChildTest() : Multiprocess(), annotation_test_() {}
~ReadFromChildTest() {}
private:
void MultiprocessParent() {
#if defined(ARCH_CPU_64_BITS)
constexpr bool am_64_bit = true;
#else
constexpr bool am_64_bit = false;
#endif
annotation_test_.ExpectAnnotations(ChildPID(), am_64_bit);
}
void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }
AnnotationTest annotation_test_;
DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest);
};
TEST(ImageAnnotationReader, ReadFromChild) {
ReadFromChildTest test;
test.Run();
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -41,6 +41,8 @@
'crashpad_info_client_options.h',
'crashpad_types/crashpad_info_reader.cc',
'crashpad_types/crashpad_info_reader.h',
'crashpad_types/image_annotation_reader.cc',
'crashpad_types/image_annotation_reader.h',
'elf/elf_dynamic_array_reader.cc',
'elf/elf_dynamic_array_reader.h',
'elf/elf_image_reader.cc',

View File

@ -73,6 +73,7 @@
'cpu_context_test.cc',
'crashpad_info_client_options_test.cc',
'crashpad_types/crashpad_info_reader_test.cc',
'crashpad_types/image_annotation_reader_test.cc',
'elf/elf_image_reader_test.cc',
'elf/elf_image_reader_test_note.S',
'linux/debug_rendezvous_test.cc',