mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +08:00
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:
parent
70563e92f4
commit
878af9cbbd
@ -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",
|
||||
|
153
snapshot/crashpad_types/image_annotation_reader.cc
Normal file
153
snapshot/crashpad_types/image_annotation_reader.cc
Normal 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), ¤t)) {
|
||||
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
|
76
snapshot/crashpad_types/image_annotation_reader.h
Normal file
76
snapshot/crashpad_types/image_annotation_reader.h
Normal 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_
|
164
snapshot/crashpad_types/image_annotation_reader_test.cc
Normal file
164
snapshot/crashpad_types/image_annotation_reader_test.cc
Normal 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
|
@ -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',
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user